added --no-overwrite option to rrdtool create. It prevents rrdtool from clobbering...
[rrdtool.git] / src / rrd_open.c
1 /*****************************************************************************
2  * RRDtool 1.4.2  Copyright by Tobi Oetiker, 1997-2009
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         if (rdwr & RRD_EXCL) {
176             flags |= O_EXCL;
177         }
178     }
179     if (rdwr & RRD_READAHEAD) {
180 #ifdef MAP_POPULATE
181         rrd_simple_file->mm_flags |= MAP_POPULATE;   /* populate ptes and data */
182 #endif
183 #if defined MAP_NONBLOCK
184         rrd_simple_file->mm_flags |= MAP_NONBLOCK;   /* just populate ptes */
185 #endif
186     }
187 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
188     flags |= O_BINARY;
189 #endif
190
191     if ((rrd_simple_file->fd = open(file_name, flags, 0666)) < 0) {
192         rrd_set_error("opening '%s': %s", file_name, rrd_strerror(errno));
193         goto out_free;
194     }
195
196 #ifdef HAVE_MMAP
197 #ifdef HAVE_BROKEN_MS_ASYNC
198     if (rdwr & RRD_READWRITE) {    
199         /* some unices, the files mtime does not get update    
200            on msync MS_ASYNC, in order to help them,     
201            we update the the timestamp at this point.      
202            The thing happens pretty 'close' to the open    
203            call so the chances of a race should be minimal.    
204                 
205            Maybe ask your vendor to fix your OS ... */    
206            utime(file_name,NULL);  
207     }
208 #endif    
209 #endif
210
211     /* Better try to avoid seeks as much as possible. stat may be heavy but
212      * many concurrent seeks are even worse.  */
213     if (newfile_size == 0 && ((fstat(rrd_simple_file->fd, &statb)) < 0)) {
214         rrd_set_error("fstat '%s': %s", file_name, rrd_strerror(errno));
215         goto out_close;
216     }
217     if (newfile_size == 0) {
218         rrd_file->file_len = statb.st_size;
219     } else {
220         rrd_file->file_len = newfile_size;
221         lseek(rrd_simple_file->fd, newfile_size - 1, SEEK_SET);
222         if ( write(rrd_simple_file->fd, "\0", 1) == -1){    /* poke */
223             rrd_set_error("write '%s': %s", file_name, rrd_strerror(errno));
224             goto out_close;
225         }
226         lseek(rrd_simple_file->fd, 0, SEEK_SET);
227     }
228 #ifdef HAVE_POSIX_FADVISE
229     /* In general we need no read-ahead when dealing with rrd_files.
230        When we stop reading, it is highly unlikely that we start up again.
231        In this manner we actually save time and diskaccess (and buffer cache).
232        Thanks to Dave Plonka for the Idea of using POSIX_FADV_RANDOM here. */
233     posix_fadvise(rrd_simple_file->fd, 0, 0, POSIX_FADV_RANDOM);
234 #endif
235
236 /*
237         if (rdwr & RRD_READWRITE)
238         {
239            if (setvbuf((rrd_simple_file->fd),NULL,_IONBF,2)) {
240                   rrd_set_error("failed to disable the stream buffer\n");
241                   return (-1);
242            }
243         }
244 */
245
246 #ifdef HAVE_MMAP
247     data = mmap(0, rrd_file->file_len, 
248         rrd_simple_file->mm_prot, rrd_simple_file->mm_flags,
249         rrd_simple_file->fd, offset);
250
251     /* lets see if the first read worked */
252     if (data == MAP_FAILED) {
253         rrd_set_error("mmaping file '%s': %s", file_name,
254                       rrd_strerror(errno));
255         goto out_close;
256     }
257     rrd_simple_file->file_start = data;
258     if (rdwr & RRD_CREAT) {
259         memset(data, DNAN, newfile_size - 1);
260         goto out_done;
261     }
262 #endif
263     if (rdwr & RRD_CREAT)
264         goto out_done;
265 #ifdef USE_MADVISE
266     if (rdwr & RRD_COPY) {
267         /* We will read everything in a moment (copying) */
268         madvise(data, rrd_file->file_len, MADV_WILLNEED );
269         madvise(data, rrd_file->file_len, MADV_SEQUENTIAL );
270     } else {
271         /* We do not need to read anything in for the moment */
272         madvise(data, rrd_file->file_len, MADV_RANDOM);
273         /* the stat_head will be needed soonish, so hint accordingly */
274         madvise(data, sizeof(stat_head_t), MADV_WILLNEED);
275         madvise(data, sizeof(stat_head_t), MADV_RANDOM);
276     }
277 #endif
278
279     __rrd_read(rrd->stat_head, stat_head_t,
280                1);
281
282     /* lets do some test if we are on track ... */
283     if (memcmp(rrd->stat_head->cookie, RRD_COOKIE, sizeof(RRD_COOKIE)) != 0) {
284         rrd_set_error("'%s' is not an RRD file", file_name);
285         goto out_nullify_head;
286     }
287
288     if (rrd->stat_head->float_cookie != FLOAT_COOKIE) {
289         rrd_set_error("This RRD was created on another architecture");
290         goto out_nullify_head;
291     }
292
293     version = atoi(rrd->stat_head->version);
294
295     if (version > atoi(RRD_VERSION)) {
296         rrd_set_error("can't handle RRD file version %s",
297                       rrd->stat_head->version);
298         goto out_nullify_head;
299     }
300 #if defined USE_MADVISE
301     /* the ds_def will be needed soonish, so hint accordingly */
302     madvise(data + PAGE_START(offset),
303             sizeof(ds_def_t) * rrd->stat_head->ds_cnt, MADV_WILLNEED);
304 #endif
305     __rrd_read(rrd->ds_def, ds_def_t,
306                rrd->stat_head->ds_cnt);
307
308 #if defined USE_MADVISE
309     /* the rra_def will be needed soonish, so hint accordingly */
310     madvise(data + PAGE_START(offset),
311             sizeof(rra_def_t) * rrd->stat_head->rra_cnt, MADV_WILLNEED);
312 #endif
313     __rrd_read(rrd->rra_def, rra_def_t,
314                rrd->stat_head->rra_cnt);
315
316     /* handle different format for the live_head */
317     if (version < 3) {
318         rrd->live_head = (live_head_t *) malloc(sizeof(live_head_t));
319         if (rrd->live_head == NULL) {
320             rrd_set_error("live_head_t malloc");
321             goto out_close;
322         }
323 #if defined USE_MADVISE
324         /* the live_head will be needed soonish, so hint accordingly */
325         madvise(data + PAGE_START(offset), sizeof(time_t), MADV_WILLNEED);
326 #endif
327         __rrd_read(rrd->legacy_last_up, time_t,
328                    1);
329
330         rrd->live_head->last_up = *rrd->legacy_last_up;
331         rrd->live_head->last_up_usec = 0;
332     } else {
333 #if defined USE_MADVISE
334         /* the live_head will be needed soonish, so hint accordingly */
335         madvise(data + PAGE_START(offset),
336                 sizeof(live_head_t), MADV_WILLNEED);
337 #endif
338         __rrd_read(rrd->live_head, live_head_t,
339                    1);
340     }
341     __rrd_read(rrd->pdp_prep, pdp_prep_t,
342                rrd->stat_head->ds_cnt);
343     __rrd_read(rrd->cdp_prep, cdp_prep_t,
344                rrd->stat_head->rra_cnt * rrd->stat_head->ds_cnt);
345     __rrd_read(rrd->rra_ptr, rra_ptr_t,
346                rrd->stat_head->rra_cnt);
347
348     rrd_file->header_len = offset;
349     rrd_file->pos = offset;
350
351     {
352       unsigned long row_cnt = 0;
353
354       for (ui=0; ui<rrd->stat_head->rra_cnt; ui++)
355         row_cnt += rrd->rra_def[ui].row_cnt;
356
357       size_t  correct_len = rrd_file->header_len +
358         sizeof(rrd_value_t) * row_cnt * rrd->stat_head->ds_cnt;
359
360       if (correct_len > rrd_file->file_len)
361       {
362         rrd_set_error("'%s' is too small (should be %ld bytes)",
363                       file_name, (long long) correct_len);
364         goto out_nullify_head;
365       }
366     }
367
368   out_done:
369     return (rrd_file);
370   out_nullify_head:
371     rrd->stat_head = NULL;
372   out_close:
373 #ifdef HAVE_MMAP
374     if (data != MAP_FAILED)
375       munmap(data, rrd_file->file_len);
376 #endif
377
378     close(rrd_simple_file->fd);
379   out_free:
380     free(rrd_file->pvt);
381     free(rrd_file);
382     return NULL;
383 }
384
385
386 #if defined DEBUG && DEBUG > 1
387 /* Print list of in-core pages of a the current rrd_file.  */
388 static
389 void mincore_print(
390     rrd_file_t *rrd_file,
391     char *mark)
392 {
393     rrd_simple_file_t *rrd_simple_file;
394     rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
395 #ifdef HAVE_MMAP
396     /* pretty print blocks in core */
397     size_t     off;
398     unsigned char *vec;
399     ssize_t   _page_size = sysconf(_SC_PAGESIZE);
400
401     off = rrd_file->file_len +
402         ((rrd_file->file_len + _page_size - 1) / _page_size);
403     vec = malloc(off);
404     if (vec != NULL) {
405         memset(vec, 0, off);
406         if (mincore(rrd_simple_file->file_start, rrd_file->file_len, vec) == 0) {
407             int       prev;
408             unsigned  is_in = 0, was_in = 0;
409
410             for (off = 0, prev = 0; off < rrd_file->file_len; ++off) {
411                 is_in = vec[off] & 1;   /* if lsb set then is core resident */
412                 if (off == 0)
413                     was_in = is_in;
414                 if (was_in != is_in) {
415                     fprintf(stderr, "%s: %sin core: %p len %ld\n", mark,
416                             was_in ? "" : "not ", vec + prev, off - prev);
417                     was_in = is_in;
418                     prev = off;
419                 }
420             }
421             fprintf(stderr,
422                     "%s: %sin core: %p len %ld\n", mark,
423                     was_in ? "" : "not ", vec + prev, off - prev);
424         } else
425             fprintf(stderr, "mincore: %s", rrd_strerror(errno));
426     }
427 #else
428     fprintf(stderr, "sorry mincore only works with mmap");
429 #endif
430 }
431 #endif                          /* defined DEBUG && DEBUG > 1 */
432
433 /*
434  * get exclusive lock to whole file.
435  * lock gets removed when we close the file
436  *
437  * returns 0 on success
438  */
439 int rrd_lock(
440     rrd_file_t *rrd_file)
441 {
442     int       rcstat;
443     rrd_simple_file_t *rrd_simple_file;
444     rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
445
446     {
447 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
448         struct _stat st;
449
450         if (_fstat(rrd_simple_file->fd, &st) == 0) {
451             rcstat = _locking(rrd_simple_file->fd, _LK_NBLCK, st.st_size);
452         } else {
453             rcstat = -1;
454         }
455 #else
456         struct flock lock;
457
458         lock.l_type = F_WRLCK;  /* exclusive write lock */
459         lock.l_len = 0; /* whole file */
460         lock.l_start = 0;   /* start of file */
461         lock.l_whence = SEEK_SET;   /* end of file */
462
463         rcstat = fcntl(rrd_simple_file->fd, F_SETLK, &lock);
464 #endif
465     }
466
467     return (rcstat);
468 }
469
470
471 /* drop cache except for the header and the active pages */
472 void rrd_dontneed(
473     rrd_file_t *rrd_file,
474     rrd_t *rrd)
475 {
476     rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
477 #if defined USE_MADVISE || defined HAVE_POSIX_FADVISE
478     size_t dontneed_start;
479     size_t rra_start;
480     size_t active_block;
481     size_t i;
482     ssize_t   _page_size = sysconf(_SC_PAGESIZE);
483
484     if (rrd_file == NULL) {
485 #if defined DEBUG && DEBUG
486             fprintf (stderr, "rrd_dontneed: Argument 'rrd_file' is NULL.\n");
487 #endif
488             return;
489     }
490
491 #if defined DEBUG && DEBUG > 1
492     mincore_print(rrd_file, "before");
493 #endif
494
495     /* ignoring errors from RRDs that are smaller then the file_len+rounding */
496     rra_start = rrd_file->header_len;
497     dontneed_start = PAGE_START(rra_start) + _page_size;
498     for (i = 0; i < rrd->stat_head->rra_cnt; ++i) {
499         active_block =
500             PAGE_START(rra_start
501                        + rrd->rra_ptr[i].cur_row
502                        * rrd->stat_head->ds_cnt * sizeof(rrd_value_t));
503         if (active_block > dontneed_start) {
504 #ifdef USE_MADVISE
505             madvise(rrd_simple_file->file_start + dontneed_start,
506                     active_block - dontneed_start - 1, MADV_DONTNEED);
507 #endif
508 /* in linux at least only fadvise DONTNEED seems to purge pages from cache */
509 #ifdef HAVE_POSIX_FADVISE
510             posix_fadvise(rrd_simple_file->fd, dontneed_start,
511                           active_block - dontneed_start - 1,
512                           POSIX_FADV_DONTNEED);
513 #endif
514         }
515         dontneed_start = active_block;
516         /* do not release 'hot' block if update for this RAA will occur
517          * within 10 minutes */
518         if (rrd->stat_head->pdp_step * rrd->rra_def[i].pdp_cnt -
519             rrd->live_head->last_up % (rrd->stat_head->pdp_step *
520                                        rrd->rra_def[i].pdp_cnt) < 10 * 60) {
521             dontneed_start += _page_size;
522         }
523         rra_start +=
524             rrd->rra_def[i].row_cnt * rrd->stat_head->ds_cnt *
525             sizeof(rrd_value_t);
526     }
527
528     if (dontneed_start < rrd_file->file_len) {
529 #ifdef USE_MADVISE
530             madvise(rrd_simple_file->file_start + dontneed_start,
531                     rrd_file->file_len - dontneed_start, MADV_DONTNEED);
532 #endif
533 #ifdef HAVE_POSIX_FADVISE
534             posix_fadvise(rrd_simple_file->fd, dontneed_start,
535                           rrd_file->file_len - dontneed_start,
536                           POSIX_FADV_DONTNEED);
537 #endif
538     }
539
540 #if defined DEBUG && DEBUG > 1
541     mincore_print(rrd_file, "after");
542 #endif
543 #endif                          /* without madvise and posix_fadvise it does not make much sense todo anything */
544 }
545
546
547
548
549
550 int rrd_close(
551     rrd_file_t *rrd_file)
552 {
553     rrd_simple_file_t *rrd_simple_file;
554     rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
555     int       ret;
556
557 #ifdef HAVE_MMAP
558     ret = msync(rrd_simple_file->file_start, rrd_file->file_len, MS_ASYNC);
559     if (ret != 0)
560         rrd_set_error("msync rrd_file: %s", rrd_strerror(errno));
561     ret = munmap(rrd_simple_file->file_start, rrd_file->file_len);
562     if (ret != 0)
563         rrd_set_error("munmap rrd_file: %s", rrd_strerror(errno));
564 #endif
565     ret = close(rrd_simple_file->fd);
566     if (ret != 0)
567         rrd_set_error("closing file: %s", rrd_strerror(errno));
568     free(rrd_file->pvt);
569     free(rrd_file);
570     rrd_file = NULL;
571     return ret;
572 }
573
574
575 /* Set position of rrd_file.  */
576
577 off_t rrd_seek(
578     rrd_file_t *rrd_file,
579     off_t off,
580     int whence)
581 {
582     off_t     ret = 0;
583     rrd_simple_file_t *rrd_simple_file;
584     rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
585
586 #ifdef HAVE_MMAP
587     if (whence == SEEK_SET)
588         rrd_file->pos = off;
589     else if (whence == SEEK_CUR)
590         rrd_file->pos += off;
591     else if (whence == SEEK_END)
592         rrd_file->pos = rrd_file->file_len + off;
593 #else
594     ret = lseek(rrd_simple_file->fd, off, whence);
595     if (ret < 0)
596         rrd_set_error("lseek: %s", rrd_strerror(errno));
597     rrd_file->pos = ret;
598 #endif
599     /* mimic fseek, which returns 0 upon success */
600     return ret < 0;     /*XXX: or just ret to mimic lseek */
601 }
602
603
604 /* Get current position in rrd_file.  */
605
606 off_t rrd_tell(
607     rrd_file_t *rrd_file)
608 {
609     return rrd_file->pos;
610 }
611
612
613 /* Read count bytes into buffer buf, starting at rrd_file->pos.
614  * Returns the number of bytes read or <0 on error.  */
615
616 ssize_t rrd_read(
617     rrd_file_t *rrd_file,
618     void *buf,
619     size_t count)
620 {
621     rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
622 #ifdef HAVE_MMAP
623     size_t    _cnt = count;
624     ssize_t   _surplus;
625
626     if (rrd_file->pos > rrd_file->file_len || _cnt == 0)    /* EOF */
627         return 0;
628     if (buf == NULL)
629         return -1;      /* EINVAL */
630     _surplus = rrd_file->pos + _cnt - rrd_file->file_len;
631     if (_surplus > 0) { /* short read */
632         _cnt -= _surplus;
633     }
634     if (_cnt == 0)
635         return 0;       /* EOF */
636     buf = memcpy(buf, rrd_simple_file->file_start + rrd_file->pos, _cnt);
637
638     rrd_file->pos += _cnt;  /* mimmic read() semantics */
639     return _cnt;
640 #else
641     ssize_t   ret;
642
643     ret = read(rrd_simple_file->fd, buf, count);
644     if (ret > 0)
645         rrd_file->pos += ret;   /* mimmic read() semantics */
646     return ret;
647 #endif
648 }
649
650
651 /* Write count bytes from buffer buf to the current position
652  * rrd_file->pos of rrd_simple_file->fd.
653  * Returns the number of bytes written or <0 on error.  */
654
655 ssize_t rrd_write(
656     rrd_file_t *rrd_file,
657     const void *buf,
658     size_t count)
659 {
660     rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
661 #ifdef HAVE_MMAP
662     size_t old_size = rrd_file->file_len;
663     if (count == 0)
664         return 0;
665     if (buf == NULL)
666         return -1;      /* EINVAL */
667     
668     if((rrd_file->pos + count) > old_size)
669     {
670         rrd_set_error("attempting to write beyond end of file");
671         return -1;
672     }
673     memcpy(rrd_simple_file->file_start + rrd_file->pos, buf, count);
674     rrd_file->pos += count;
675     return count;       /* mimmic write() semantics */
676 #else
677     ssize_t   _sz = write(rrd_simple_file->fd, buf, count);
678
679     if (_sz > 0)
680         rrd_file->pos += _sz;
681     return _sz;
682 #endif
683 }
684
685
686 /* this is a leftover from the old days, it serves no purpose
687    and is therefore turned into a no-op */
688 void rrd_flush(
689     rrd_file_t *rrd_file  __attribute__((unused)))
690 {
691 }
692
693 /* Initialize RRD header.  */
694
695 void rrd_init(
696     rrd_t *rrd)
697 {
698     rrd->stat_head = NULL;
699     rrd->ds_def = NULL;
700     rrd->rra_def = NULL;
701     rrd->live_head = NULL;
702     rrd->legacy_last_up = NULL;
703     rrd->rra_ptr = NULL;
704     rrd->pdp_prep = NULL;
705     rrd->cdp_prep = NULL;
706     rrd->rrd_value = NULL;
707 }
708
709
710 /* free RRD header data.  */
711
712 #ifdef HAVE_MMAP
713 void rrd_free(
714     rrd_t *rrd)
715 {
716     if (rrd->legacy_last_up) {  /* this gets set for version < 3 only */
717         free(rrd->live_head);
718     }
719 }
720 #else
721 void rrd_free(
722     rrd_t *rrd)
723 {
724     free(rrd->live_head);
725     free(rrd->stat_head);
726     free(rrd->ds_def);
727     free(rrd->rra_def);
728     free(rrd->rra_ptr);
729     free(rrd->pdp_prep);
730     free(rrd->cdp_prep);
731     free(rrd->rrd_value);
732 }
733 #endif
734
735
736 /* routine used by external libraries to free memory allocated by
737  * rrd library */
738
739 void rrd_freemem(
740     void *mem)
741 {
742     free(mem);
743 }
744
745 /*
746  * rra_update informs us about the RRAs being updated
747  * The low level storage API may use this information for
748  * aligning RRAs within stripes, or other performance enhancements
749  */
750 void rrd_notify_row(
751     rrd_file_t *rrd_file  __attribute__((unused)),
752     int rra_idx  __attribute__((unused)),
753     unsigned long rra_row  __attribute__((unused)),
754     time_t rra_time  __attribute__((unused)))
755 {
756 }
757
758 /*
759  * This function is called when creating a new RRD
760  * The storage implementation can use this opportunity to select
761  * a sensible starting row within the file.
762  * The default implementation is random, to ensure that all RRAs
763  * don't change to a new disk block at the same time
764  */
765 unsigned long rrd_select_initial_row(
766     rrd_file_t *rrd_file  __attribute__((unused)),
767     int rra_idx  __attribute__((unused)),
768     rra_def_t *rra
769     )
770 {
771     return rrd_random() % rra->row_cnt;
772 }