Code

Imported Upstream version 1.4.7
[pkg-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: rrd_open.c 2267 2012-01-24 10:08:48Z oetiker $
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         lseek(rrd_simple_file->fd, newfile_size - 1, SEEK_SET);
225         if ( write(rrd_simple_file->fd, "\0", 1) == -1){    /* poke */
226             rrd_set_error("write '%s': %s", file_name, rrd_strerror(errno));
227             goto out_close;
228         }
229         lseek(rrd_simple_file->fd, 0, SEEK_SET);
230     }
231 #ifdef HAVE_POSIX_FADVISE
232     /* In general we need no read-ahead when dealing with rrd_files.
233        When we stop reading, it is highly unlikely that we start up again.
234        In this manner we actually save time and diskaccess (and buffer cache).
235        Thanks to Dave Plonka for the Idea of using POSIX_FADV_RANDOM here. */
236     posix_fadvise(rrd_simple_file->fd, 0, 0, POSIX_FADV_RANDOM);
237 #endif
239 /*
240         if (rdwr & RRD_READWRITE)
241         {
242            if (setvbuf((rrd_simple_file->fd),NULL,_IONBF,2)) {
243                   rrd_set_error("failed to disable the stream buffer\n");
244                   return (-1);
245            }
246         }
247 */
249 #ifdef HAVE_MMAP
250         /* force allocating the file on the underlaying filesystem to prevent any
251          * future bus error when the filesystem is full and attempting to write
252          * trough the file mapping. Filling the file using memset on the file
253          * mapping can also lead some bus error, so we use the old fashioned
254          * write().
255          */
256     if (rdwr & RRD_CREAT) {
257                 char     buf[4096];
258                 unsigned i;
260                 memset(buf, DNAN, sizeof buf);
261                 lseek(rrd_simple_file->fd, offset, SEEK_SET);
262         
263                 for (i = 0; i < (newfile_size - 1) / sizeof buf; ++i)
264                 {
265                         if (write(rrd_simple_file->fd, buf, sizeof buf) == -1)
266                         {
267                                 rrd_set_error("write '%s': %s", file_name, rrd_strerror(errno));
268                                 goto out_close;
269                         }
270                 }
271                 
272                 if (write(rrd_simple_file->fd, buf,
273                                         (newfile_size - 1) % sizeof buf) == -1)
274                 {
275                         rrd_set_error("write '%s': %s", file_name, rrd_strerror(errno));
276                         goto out_close;
277                 }
279                 lseek(rrd_simple_file->fd, 0, SEEK_SET);
280     }
282     data = mmap(0, rrd_file->file_len, 
283         rrd_simple_file->mm_prot, rrd_simple_file->mm_flags,
284         rrd_simple_file->fd, offset);
286     /* lets see if the first read worked */
287     if (data == MAP_FAILED) {
288         rrd_set_error("mmaping file '%s': %s", file_name,
289                       rrd_strerror(errno));
290         goto out_close;
291     }
292     rrd_simple_file->file_start = data;
293     if (rdwr & RRD_CREAT) {
294         goto out_done;
295     }
296 #endif
297     if (rdwr & RRD_CREAT)
298         goto out_done;
299 #ifdef USE_MADVISE
300     if (rdwr & RRD_COPY) {
301         /* We will read everything in a moment (copying) */
302         madvise(data, rrd_file->file_len, MADV_WILLNEED );
303         madvise(data, rrd_file->file_len, MADV_SEQUENTIAL );
304     } else {
305         /* We do not need to read anything in for the moment */
306         madvise(data, rrd_file->file_len, MADV_RANDOM);
307         /* the stat_head will be needed soonish, so hint accordingly */
308         madvise(data, sizeof(stat_head_t), MADV_WILLNEED);
309         madvise(data, sizeof(stat_head_t), MADV_RANDOM);
310     }
311 #endif
313     __rrd_read(rrd->stat_head, stat_head_t,
314                1);
316     /* lets do some test if we are on track ... */
317     if (memcmp(rrd->stat_head->cookie, RRD_COOKIE, sizeof(RRD_COOKIE)) != 0) {
318         rrd_set_error("'%s' is not an RRD file", file_name);
319         goto out_nullify_head;
320     }
322     if (rrd->stat_head->float_cookie != FLOAT_COOKIE) {
323         rrd_set_error("This RRD was created on another architecture");
324         goto out_nullify_head;
325     }
327     version = atoi(rrd->stat_head->version);
329     if (version > atoi(RRD_VERSION)) {
330         rrd_set_error("can't handle RRD file version %s",
331                       rrd->stat_head->version);
332         goto out_nullify_head;
333     }
334 #if defined USE_MADVISE
335     /* the ds_def will be needed soonish, so hint accordingly */
336     madvise(data + PAGE_START(offset),
337             sizeof(ds_def_t) * rrd->stat_head->ds_cnt, MADV_WILLNEED);
338 #endif
339     __rrd_read(rrd->ds_def, ds_def_t,
340                rrd->stat_head->ds_cnt);
342 #if defined USE_MADVISE
343     /* the rra_def will be needed soonish, so hint accordingly */
344     madvise(data + PAGE_START(offset),
345             sizeof(rra_def_t) * rrd->stat_head->rra_cnt, MADV_WILLNEED);
346 #endif
347     __rrd_read(rrd->rra_def, rra_def_t,
348                rrd->stat_head->rra_cnt);
350     /* handle different format for the live_head */
351     if (version < 3) {
352         rrd->live_head = (live_head_t *) malloc(sizeof(live_head_t));
353         if (rrd->live_head == NULL) {
354             rrd_set_error("live_head_t malloc");
355             goto out_close;
356         }
357 #if defined USE_MADVISE
358         /* the live_head will be needed soonish, so hint accordingly */
359         madvise(data + PAGE_START(offset), sizeof(time_t), MADV_WILLNEED);
360 #endif
361         __rrd_read(rrd->legacy_last_up, time_t,
362                    1);
364         rrd->live_head->last_up = *rrd->legacy_last_up;
365         rrd->live_head->last_up_usec = 0;
366     } else {
367 #if defined USE_MADVISE
368         /* the live_head will be needed soonish, so hint accordingly */
369         madvise(data + PAGE_START(offset),
370                 sizeof(live_head_t), MADV_WILLNEED);
371 #endif
372         __rrd_read(rrd->live_head, live_head_t,
373                    1);
374     }
375     __rrd_read(rrd->pdp_prep, pdp_prep_t,
376                rrd->stat_head->ds_cnt);
377     __rrd_read(rrd->cdp_prep, cdp_prep_t,
378                rrd->stat_head->rra_cnt * rrd->stat_head->ds_cnt);
379     __rrd_read(rrd->rra_ptr, rra_ptr_t,
380                rrd->stat_head->rra_cnt);
382     rrd_file->header_len = offset;
383     rrd_file->pos = offset;
385     {
386       unsigned long row_cnt = 0;
388       for (ui=0; ui<rrd->stat_head->rra_cnt; ui++)
389         row_cnt += rrd->rra_def[ui].row_cnt;
391       size_t  correct_len = rrd_file->header_len +
392         sizeof(rrd_value_t) * row_cnt * rrd->stat_head->ds_cnt;
394       if (correct_len > rrd_file->file_len)
395       {
396         rrd_set_error("'%s' is too small (should be %ld bytes)",
397                       file_name, (long long) correct_len);
398         goto out_nullify_head;
399       }
400     }
402   out_done:
403     return (rrd_file);
404   out_nullify_head:
405     rrd->stat_head = NULL;
406   out_close:
407 #ifdef HAVE_MMAP
408     if (data != MAP_FAILED)
409       munmap(data, rrd_file->file_len);
410 #endif
412     close(rrd_simple_file->fd);
413   out_free:
414     free(rrd_file->pvt);
415     free(rrd_file);
416     return NULL;
420 #if defined DEBUG && DEBUG > 1
421 /* Print list of in-core pages of a the current rrd_file.  */
422 static
423 void mincore_print(
424     rrd_file_t *rrd_file,
425     char *mark)
427     rrd_simple_file_t *rrd_simple_file;
428     rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
429 #ifdef HAVE_MMAP
430     /* pretty print blocks in core */
431     size_t     off;
432     unsigned char *vec;
433     ssize_t   _page_size = sysconf(_SC_PAGESIZE);
435     off = rrd_file->file_len +
436         ((rrd_file->file_len + _page_size - 1) / _page_size);
437     vec = malloc(off);
438     if (vec != NULL) {
439         memset(vec, 0, off);
440         if (mincore(rrd_simple_file->file_start, rrd_file->file_len, vec) == 0) {
441             int       prev;
442             unsigned  is_in = 0, was_in = 0;
444             for (off = 0, prev = 0; off < rrd_file->file_len; ++off) {
445                 is_in = vec[off] & 1;   /* if lsb set then is core resident */
446                 if (off == 0)
447                     was_in = is_in;
448                 if (was_in != is_in) {
449                     fprintf(stderr, "%s: %sin core: %p len %ld\n", mark,
450                             was_in ? "" : "not ", vec + prev, off - prev);
451                     was_in = is_in;
452                     prev = off;
453                 }
454             }
455             fprintf(stderr,
456                     "%s: %sin core: %p len %ld\n", mark,
457                     was_in ? "" : "not ", vec + prev, off - prev);
458         } else
459             fprintf(stderr, "mincore: %s", rrd_strerror(errno));
460     }
461 #else
462     fprintf(stderr, "sorry mincore only works with mmap");
463 #endif
465 #endif                          /* defined DEBUG && DEBUG > 1 */
467 /*
468  * get exclusive lock to whole file.
469  * lock gets removed when we close the file
470  *
471  * returns 0 on success
472  */
473 int rrd_lock(
474     rrd_file_t *rrd_file)
476     int       rcstat;
477     rrd_simple_file_t *rrd_simple_file;
478     rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
480     {
481 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
482         struct _stat st;
484         if (_fstat(rrd_simple_file->fd, &st) == 0) {
485             rcstat = _locking(rrd_simple_file->fd, _LK_NBLCK, st.st_size);
486         } else {
487             rcstat = -1;
488         }
489 #else
490         struct flock lock;
492         lock.l_type = F_WRLCK;  /* exclusive write lock */
493         lock.l_len = 0; /* whole file */
494         lock.l_start = 0;   /* start of file */
495         lock.l_whence = SEEK_SET;   /* end of file */
497         rcstat = fcntl(rrd_simple_file->fd, F_SETLK, &lock);
498 #endif
499     }
501     return (rcstat);
505 /* drop cache except for the header and the active pages */
506 void rrd_dontneed(
507     rrd_file_t *rrd_file,
508     rrd_t *rrd)
510     rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
511 #if defined USE_MADVISE || defined HAVE_POSIX_FADVISE
512     size_t dontneed_start;
513     size_t rra_start;
514     size_t active_block;
515     size_t i;
516     ssize_t   _page_size = sysconf(_SC_PAGESIZE);
518     if (rrd_file == NULL) {
519 #if defined DEBUG && DEBUG
520             fprintf (stderr, "rrd_dontneed: Argument 'rrd_file' is NULL.\n");
521 #endif
522             return;
523     }
525 #if defined DEBUG && DEBUG > 1
526     mincore_print(rrd_file, "before");
527 #endif
529     /* ignoring errors from RRDs that are smaller then the file_len+rounding */
530     rra_start = rrd_file->header_len;
531     dontneed_start = PAGE_START(rra_start) + _page_size;
532     for (i = 0; i < rrd->stat_head->rra_cnt; ++i) {
533         active_block =
534             PAGE_START(rra_start
535                        + rrd->rra_ptr[i].cur_row
536                        * rrd->stat_head->ds_cnt * sizeof(rrd_value_t));
537         if (active_block > dontneed_start) {
538 #ifdef USE_MADVISE
539             madvise(rrd_simple_file->file_start + dontneed_start,
540                     active_block - dontneed_start - 1, MADV_DONTNEED);
541 #endif
542 /* in linux at least only fadvise DONTNEED seems to purge pages from cache */
543 #ifdef HAVE_POSIX_FADVISE
544             posix_fadvise(rrd_simple_file->fd, dontneed_start,
545                           active_block - dontneed_start - 1,
546                           POSIX_FADV_DONTNEED);
547 #endif
548         }
549         dontneed_start = active_block;
550         /* do not release 'hot' block if update for this RAA will occur
551          * within 10 minutes */
552         if (rrd->stat_head->pdp_step * rrd->rra_def[i].pdp_cnt -
553             rrd->live_head->last_up % (rrd->stat_head->pdp_step *
554                                        rrd->rra_def[i].pdp_cnt) < 10 * 60) {
555             dontneed_start += _page_size;
556         }
557         rra_start +=
558             rrd->rra_def[i].row_cnt * rrd->stat_head->ds_cnt *
559             sizeof(rrd_value_t);
560     }
562     if (dontneed_start < rrd_file->file_len) {
563 #ifdef USE_MADVISE
564             madvise(rrd_simple_file->file_start + dontneed_start,
565                     rrd_file->file_len - dontneed_start, MADV_DONTNEED);
566 #endif
567 #ifdef HAVE_POSIX_FADVISE
568             posix_fadvise(rrd_simple_file->fd, dontneed_start,
569                           rrd_file->file_len - dontneed_start,
570                           POSIX_FADV_DONTNEED);
571 #endif
572     }
574 #if defined DEBUG && DEBUG > 1
575     mincore_print(rrd_file, "after");
576 #endif
577 #endif                          /* without madvise and posix_fadvise it does not make much sense todo anything */
584 int rrd_close(
585     rrd_file_t *rrd_file)
587     rrd_simple_file_t *rrd_simple_file;
588     rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
589     int       ret;
591 #ifdef HAVE_MMAP
592     ret = msync(rrd_simple_file->file_start, rrd_file->file_len, MS_ASYNC);
593     if (ret != 0)
594         rrd_set_error("msync rrd_file: %s", rrd_strerror(errno));
595     ret = munmap(rrd_simple_file->file_start, rrd_file->file_len);
596     if (ret != 0)
597         rrd_set_error("munmap rrd_file: %s", rrd_strerror(errno));
598 #endif
599     ret = close(rrd_simple_file->fd);
600     if (ret != 0)
601         rrd_set_error("closing file: %s", rrd_strerror(errno));
602     free(rrd_file->pvt);
603     free(rrd_file);
604     rrd_file = NULL;
605     return ret;
609 /* Set position of rrd_file.  */
611 off_t rrd_seek(
612     rrd_file_t *rrd_file,
613     off_t off,
614     int whence)
616     off_t     ret = 0;
617     rrd_simple_file_t *rrd_simple_file;
618     rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
620 #ifdef HAVE_MMAP
621     if (whence == SEEK_SET)
622         rrd_file->pos = off;
623     else if (whence == SEEK_CUR)
624         rrd_file->pos += off;
625     else if (whence == SEEK_END)
626         rrd_file->pos = rrd_file->file_len + off;
627 #else
628     ret = lseek(rrd_simple_file->fd, off, whence);
629     if (ret < 0)
630         rrd_set_error("lseek: %s", rrd_strerror(errno));
631     rrd_file->pos = ret;
632 #endif
633     /* mimic fseek, which returns 0 upon success */
634     return ret < 0;     /*XXX: or just ret to mimic lseek */
638 /* Get current position in rrd_file.  */
640 off_t rrd_tell(
641     rrd_file_t *rrd_file)
643     return rrd_file->pos;
647 /* Read count bytes into buffer buf, starting at rrd_file->pos.
648  * Returns the number of bytes read or <0 on error.  */
650 ssize_t rrd_read(
651     rrd_file_t *rrd_file,
652     void *buf,
653     size_t count)
655     rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
656 #ifdef HAVE_MMAP
657     size_t    _cnt = count;
658     ssize_t   _surplus;
660     if (rrd_file->pos > rrd_file->file_len || _cnt == 0)    /* EOF */
661         return 0;
662     if (buf == NULL)
663         return -1;      /* EINVAL */
664     _surplus = rrd_file->pos + _cnt - rrd_file->file_len;
665     if (_surplus > 0) { /* short read */
666         _cnt -= _surplus;
667     }
668     if (_cnt == 0)
669         return 0;       /* EOF */
670     buf = memcpy(buf, rrd_simple_file->file_start + rrd_file->pos, _cnt);
672     rrd_file->pos += _cnt;  /* mimmic read() semantics */
673     return _cnt;
674 #else
675     ssize_t   ret;
677     ret = read(rrd_simple_file->fd, buf, count);
678     if (ret > 0)
679         rrd_file->pos += ret;   /* mimmic read() semantics */
680     return ret;
681 #endif
685 /* Write count bytes from buffer buf to the current position
686  * rrd_file->pos of rrd_simple_file->fd.
687  * Returns the number of bytes written or <0 on error.  */
689 ssize_t rrd_write(
690     rrd_file_t *rrd_file,
691     const void *buf,
692     size_t count)
694     rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
695 #ifdef HAVE_MMAP
696     size_t old_size = rrd_file->file_len;
697     if (count == 0)
698         return 0;
699     if (buf == NULL)
700         return -1;      /* EINVAL */
701     
702     if((rrd_file->pos + count) > old_size)
703     {
704         rrd_set_error("attempting to write beyond end of file");
705         return -1;
706     }
707     memcpy(rrd_simple_file->file_start + rrd_file->pos, buf, count);
708     rrd_file->pos += count;
709     return count;       /* mimmic write() semantics */
710 #else
711     ssize_t   _sz = write(rrd_simple_file->fd, buf, count);
713     if (_sz > 0)
714         rrd_file->pos += _sz;
715     return _sz;
716 #endif
720 /* this is a leftover from the old days, it serves no purpose
721    and is therefore turned into a no-op */
722 void rrd_flush(
723     rrd_file_t UNUSED(*rrd_file))
727 /* Initialize RRD header.  */
729 void rrd_init(
730     rrd_t *rrd)
732     rrd->stat_head = NULL;
733     rrd->ds_def = NULL;
734     rrd->rra_def = NULL;
735     rrd->live_head = NULL;
736     rrd->legacy_last_up = NULL;
737     rrd->rra_ptr = NULL;
738     rrd->pdp_prep = NULL;
739     rrd->cdp_prep = NULL;
740     rrd->rrd_value = NULL;
744 /* free RRD header data.  */
746 #ifdef HAVE_MMAP
747 void rrd_free(
748     rrd_t *rrd)
750     if (rrd->legacy_last_up) {  /* this gets set for version < 3 only */
751         free(rrd->live_head);
752     }
754 #else
755 void rrd_free(
756     rrd_t *rrd)
758     free(rrd->live_head);
759     free(rrd->stat_head);
760     free(rrd->ds_def);
761     free(rrd->rra_def);
762     free(rrd->rra_ptr);
763     free(rrd->pdp_prep);
764     free(rrd->cdp_prep);
765     free(rrd->rrd_value);
767 #endif
770 /* routine used by external libraries to free memory allocated by
771  * rrd library */
773 void rrd_freemem(
774     void *mem)
776     free(mem);
779 /*
780  * rra_update informs us about the RRAs being updated
781  * The low level storage API may use this information for
782  * aligning RRAs within stripes, or other performance enhancements
783  */
784 void rrd_notify_row(
785     rrd_file_t UNUSED(*rrd_file),
786     int UNUSED(rra_idx),
787     unsigned long UNUSED(rra_row),
788     time_t UNUSED(rra_time))
792 /*
793  * This function is called when creating a new RRD
794  * The storage implementation can use this opportunity to select
795  * a sensible starting row within the file.
796  * The default implementation is random, to ensure that all RRAs
797  * don't change to a new disk block at the same time
798  */
799 unsigned long rrd_select_initial_row(
800     rrd_file_t UNUSED(*rrd_file),
801     int UNUSED(rra_idx),
802     rra_def_t *rra
803     )
805     return rrd_random() % rra->row_cnt;