1 /* $Id: cdb_init.c,v 1.9 2003/11/03 21:42:20 mjt Exp $
2  * cdb_init, cdb_free and cdb_read routines
3  *
4  * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
5  * Public domain.
6  */
7 
8 #include <config.h>
9 
10 #include <sys/types.h>
11 #ifdef HAVE_MMAP
12 # include <sys/mman.h>
13 #else
14 # include "safeunistd.h"
15 # include <cstdlib>
16 #endif
17 #ifdef __WIN32__
18 # include "safewindows.h"
19 #endif
20 #include "safesysstat.h"
21 #include "cdb_int.h"
22 #include <cstring>
23 
24 using namespace std;
25 
26 int
cdb_init(struct cdb * cdbp,int fd)27 cdb_init(struct cdb *cdbp, int fd)
28 {
29   struct stat st;
30   unsigned char *mem;
31   unsigned fsize, dend;
32 #ifndef HAVE_MMAP
33 #ifdef _WIN32
34   HANDLE hFile, hMapping;
35 #else
36   size_t size;
37   unsigned char *p;
38 #endif
39 #endif
40 
41   /* get file size */
42   if (fstat(fd, &st) < 0)
43     return -1;
44   /* trivial sanity check: at least toc should be here */
45   if (st.st_size < 2048)
46     return errno = EPROTO, -1;
47   fsize = (unsigned)(st.st_size & 0xffffffffu);
48   /* memory-map file */
49 #ifndef HAVE_MMAP
50 #ifdef _WIN32
51   hFile = (HANDLE) _get_osfhandle(fd);
52   if(hFile == (HANDLE) -1) return -1;
53   hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
54   if (!hMapping) return -1;
55   mem = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
56   if (!mem) return -1;
57 #else
58   // No mmap, so take the very crude approach of malloc and read the whole file in!
59   if ((mem = (unsigned char *)malloc(fsize)) == NULL)
60     return -1;
61   size = fsize;
62   p = mem;
63   while (size > 0) {
64     ssize_t n = read(fd, (void*)p, size);
65     if (n == -1)
66       return -1;
67     p += n;
68     size -= n;
69   }
70 #endif
71 #else
72   mem = (unsigned char *)mmap(NULL, fsize, PROT_READ, MAP_SHARED, fd, 0);
73   if (mem == (unsigned char *)-1)
74     return -1;
75 #endif /* _WIN32 */
76 
77   cdbp->cdb_fd = fd;
78   cdbp->cdb_fsize = fsize;
79   cdbp->cdb_mem = mem;
80 
81 #if 0
82   /* XXX don't know well about madvise syscall -- is it legal
83      to set different options for parts of one mmap() region?
84      There is also posix_madvise() exist, with POSIX_MADV_RANDOM etc...
85   */
86 #ifdef MADV_RANDOM
87   /* set madvise() parameters. Ignore errors for now if system
88      doesn't support it */
89   madvise(mem, 2048, MADV_WILLNEED);
90   madvise(mem + 2048, cdbp->cdb_fsize - 2048, MADV_RANDOM);
91 #endif
92 #endif
93 
94   cdbp->cdb_vpos = cdbp->cdb_vlen = 0;
95   cdbp->cdb_kpos = cdbp->cdb_klen = 0;
96   dend = cdb_unpack(mem);
97   if (dend < 2048) dend = 2048;
98   else if (dend >= fsize) dend = fsize;
99   cdbp->cdb_dend = dend;
100 
101   return 0;
102 }
103 
104 #ifdef __cplusplus
105 class VoidStarOrCharStar {
106     void *p;
107   public:
VoidStarOrCharStar(const void * p_)108     VoidStarOrCharStar(const void *p_) : p(const_cast<void*>(p_)) { }
VoidStarOrCharStar(const char * p_)109     VoidStarOrCharStar(const char *p_) : p(const_cast<char*>(p_)) { }
operator void*()110     operator void*() { return p; }
operator char*()111     operator char*() { return static_cast<char*>(p); }
112 };
113 #endif
114 
115 void
cdb_free(struct cdb * cdbp)116 cdb_free(struct cdb *cdbp)
117 {
118   if (cdbp->cdb_mem) {
119 #ifdef _WIN32
120     HANDLE hFile, hMapping;
121 #endif
122 
123 #ifndef HAVE_MMAP
124 #ifdef __cplusplus
125     void * p = const_cast<void*>((const void*)cdbp->cdb_mem);
126 #else
127     void * p = (void*)cdbp->cdb_mem;
128 #endif
129 #ifdef _WIN32
130     hFile = (HANDLE) _get_osfhandle(cdbp->cdb_fd);
131     hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
132     UnmapViewOfFile(p);
133     CloseHandle(hMapping);
134 #else
135     free(p);
136 #endif
137 #else
138 #ifdef __cplusplus
139     /* Solaris sys/mman.h defines munmap as taking char* unless __STDC__ is
140      * defined (which it isn't in C++).
141      */
142     VoidStarOrCharStar p(cdbp->cdb_mem);
143 #else
144     void * p = (void*)cdbp->cdb_mem;
145 #endif
146     munmap(p, cdbp->cdb_fsize);
147 #endif /* _WIN32 */
148     cdbp->cdb_mem = NULL;
149   }
150   cdbp->cdb_fsize = 0;
151 }
152 
153 const void *
cdb_get(const struct cdb * cdbp,unsigned len,unsigned pos)154 cdb_get(const struct cdb *cdbp, unsigned len, unsigned pos)
155 {
156   if (pos > cdbp->cdb_fsize || cdbp->cdb_fsize - pos < len) {
157     errno = EPROTO;
158     return NULL;
159   }
160   return cdbp->cdb_mem + pos;
161 }
162 
163 int
cdb_read(const struct cdb * cdbp,void * buf,unsigned len,unsigned pos)164 cdb_read(const struct cdb *cdbp, void *buf, unsigned len, unsigned pos)
165 {
166   const void *data = cdb_get(cdbp, len, pos);
167   if (!data) return -1;
168   memcpy(buf, data, len);
169   return 0;
170 }
171