1 /* MDB Tools - A library for reading MS Access database files
2 * Copyright (C) 2000 Brian Bruns
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #include "mdbtools.h"
21
22 #ifdef DMALLOC
23 #include "dmalloc.h"
24 #endif
25
26 /*
27 typedef struct {
28 int pg_size;
29 guint16 row_count_offset;
30 guint16 tab_num_rows_offset;
31 guint16 tab_num_cols_offset;
32 guint16 tab_num_idxs_offset;
33 guint16 tab_num_ridxs_offset;
34 guint16 tab_usage_map_offset;
35 guint16 tab_first_dpg_offset;
36 guint16 tab_cols_start_offset;
37 guint16 tab_ridx_entry_size;
38 guint16 col_fixed_offset;
39 guint16 col_size_offset;
40 guint16 col_num_offset;
41 guint16 tab_col_entry_size;
42 guint16 tab_free_map_offset;
43 guint16 tab_col_offset_var;
44 guint16 tab_col_offset_fixed;
45 guint16 tab_row_col_num_offset;
46 } MdbFormatConstants;
47 */
48 MdbFormatConstants MdbJet4Constants = {
49 4096, 0x0c, 16, 45, 47, 51, 55, 56, 63, 12, 15, 23, 5, 25, 59, 7, 21, 9
50 };
51 MdbFormatConstants MdbJet3Constants = {
52 2048, 0x08, 12, 25, 27, 31, 35, 36, 43, 8, 13, 16, 1, 18, 39, 3, 14, 5
53 };
54
55 static ssize_t _mdb_read_pg(MdbHandle *mdb, unsigned char *pg_buf, unsigned long pg);
56
57 /**
58 * mdb_find_file:
59 * @filename: path to MDB (database) file
60 *
61 * Finds and returns the absolute path to an MDB file. Function will first try
62 * to fstat file as passed, then search through the $MDBPATH if not found.
63 *
64 * Return value: gchar pointer to absolute path. Caller is responsible for
65 * freeing.
66 **/
67
mdb_find_file(const char * file_name)68 static gchar *mdb_find_file(const char *file_name)
69 {
70 struct stat status;
71 gchar *mdbpath, **dir, *tmpfname;
72 unsigned int i = 0;
73
74 /* try the provided file name first */
75 if (!stat(file_name, &status)) {
76 return g_strdup(file_name);
77 }
78
79 /* Now pull apart $MDBPATH and try those */
80 mdbpath = (gchar *) getenv("MDBPATH");
81 /* no path, can't find file */
82 if (!mdbpath || !strlen(mdbpath)) return NULL;
83
84 dir = g_strsplit(mdbpath, ":", 0);
85 while (dir[i]) {
86 if (!strlen(dir[i])) continue;
87 tmpfname = g_strconcat(dir[i++], "/", file_name, NULL);
88 if (!stat(tmpfname, &status)) {
89 g_strfreev(dir);
90 return tmpfname;
91 }
92 g_free(tmpfname);
93 }
94 g_strfreev(dir);
95 return NULL;
96 }
97 /**
98 * mdb_open:
99 * @filename: path to MDB (database) file
100 * @flags: MDB_NOFLAGS for read-only, MDB_WRITABLE for read/write
101 *
102 * Opens an MDB file and returns an MdbHandle to it. MDB File may be relative
103 * to the current directory, a full path to the file, or relative to a
104 * component of $MDBPATH.
105 *
106 * Return value: pointer to MdbHandle structure.
107 **/
mdb_open(const char * filename,MdbFileFlags flags)108 MdbHandle *mdb_open(const char *filename, MdbFileFlags flags)
109 {
110 MdbHandle *mdb;
111 int open_flags;
112
113 mdb = (MdbHandle *) g_malloc0(sizeof(MdbHandle));
114 mdb_set_default_backend(mdb, "access");
115 #ifdef HAVE_ICONV
116 mdb->iconv_in = (iconv_t)-1;
117 mdb->iconv_out = (iconv_t)-1;
118 #endif
119 /* need something to bootstrap with, reassign after page 0 is read */
120 mdb->fmt = &MdbJet3Constants;
121 mdb->f = (MdbFile *) g_malloc0(sizeof(MdbFile));
122 mdb->f->refs = 1;
123 mdb->f->fd = -1;
124 mdb->f->filename = (char *) mdb_find_file(filename);
125 if (!mdb->f->filename) {
126 fprintf(stderr, "Can't alloc filename\n");
127 mdb_close(mdb);
128 return NULL;
129 }
130 if (flags & MDB_WRITABLE) {
131 mdb->f->writable = TRUE;
132 open_flags = O_RDWR;
133 } else {
134 open_flags = O_RDONLY;
135 }
136
137 #ifdef _WIN32
138 open_flags |= O_BINARY;
139 #endif
140
141 mdb->f->fd = open(mdb->f->filename, open_flags);
142
143 if (mdb->f->fd==-1) {
144 fprintf(stderr,"Couldn't open file %s\n",mdb->f->filename);
145 mdb_close(mdb);
146 return NULL;
147 }
148 if (!mdb_read_pg(mdb, 0)) {
149 fprintf(stderr,"Couldn't read first page.\n");
150 mdb_close(mdb);
151 return NULL;
152 }
153 if (mdb->pg_buf[0] != 0) {
154 mdb_close(mdb);
155 return NULL;
156 }
157 mdb->f->jet_version = mdb_get_int32(mdb->pg_buf, 0x14);
158 if (IS_JET4(mdb)) {
159 mdb->fmt = &MdbJet4Constants;
160 } else if (IS_JET3(mdb)) {
161 mdb->fmt = &MdbJet3Constants;
162 } else {
163 fprintf(stderr,"Unknown Jet version.\n");
164 mdb_close(mdb);
165 return NULL;
166 }
167 mdb_iconv_init(mdb);
168
169 return mdb;
170 }
171
172 /**
173 * mdb_close:
174 * @mdb: Handle to open MDB database file
175 *
176 * Dereferences MDB file, closes if reference count is 0, and destroys handle.
177 *
178 **/
179 void
mdb_close(MdbHandle * mdb)180 mdb_close(MdbHandle *mdb)
181 {
182 if (!mdb) return;
183 mdb_free_catalog(mdb);
184 g_free(mdb->stats);
185 g_free(mdb->backend_name);
186
187 if (mdb->f) {
188 if (mdb->f->refs > 1) {
189 mdb->f->refs--;
190 } else {
191 if (mdb->f->fd != -1) close(mdb->f->fd);
192 g_free(mdb->f->filename);
193 g_free(mdb->f);
194 }
195 }
196
197 mdb_iconv_close(mdb);
198
199 g_free(mdb);
200 }
201 /**
202 * mdb_clone_handle:
203 * @mdb: Handle to open MDB database file
204 *
205 * Clones an existing database handle. Cloned handle shares the file descriptor
206 * but has its own page buffer, page position, and similar internal variables.
207 *
208 * Return value: new handle to the database.
209 */
mdb_clone_handle(MdbHandle * mdb)210 MdbHandle *mdb_clone_handle(MdbHandle *mdb)
211 {
212 MdbHandle *newmdb;
213 MdbCatalogEntry *entry, *data;
214 unsigned int i;
215
216 newmdb = (MdbHandle *) g_memdup(mdb, sizeof(MdbHandle));
217 newmdb->stats = NULL;
218 newmdb->catalog = g_ptr_array_new();
219 for (i=0;i<mdb->num_catalog;i++) {
220 entry = g_ptr_array_index(mdb->catalog,i);
221 data = g_memdup(entry,sizeof(MdbCatalogEntry));
222 g_ptr_array_add(newmdb->catalog, data);
223 }
224 mdb->backend_name = NULL;
225 if (mdb->f) {
226 mdb->f->refs++;
227 }
228 mdb_iconv_init(mdb);
229
230 return newmdb;
231 }
232
233 /*
234 ** mdb_read a wrapper for read that bails if anything is wrong
235 */
mdb_read_pg(MdbHandle * mdb,unsigned long pg)236 ssize_t mdb_read_pg(MdbHandle *mdb, unsigned long pg)
237 {
238 ssize_t len;
239
240 if (pg && mdb->cur_pg == pg) return mdb->fmt->pg_size;
241
242 len = _mdb_read_pg(mdb, mdb->pg_buf, pg);
243 //fprintf(stderr, "read page %d type %02x\n", pg, mdb->pg_buf[0]);
244 mdb->cur_pg = pg;
245 /* kan - reset the cur_pos on a new page read */
246 mdb->cur_pos = 0; /* kan */
247 return len;
248 }
mdb_read_alt_pg(MdbHandle * mdb,unsigned long pg)249 ssize_t mdb_read_alt_pg(MdbHandle *mdb, unsigned long pg)
250 {
251 ssize_t len;
252
253 len = _mdb_read_pg(mdb, mdb->alt_pg_buf, pg);
254 return len;
255 }
_mdb_read_pg(MdbHandle * mdb,unsigned char * pg_buf,unsigned long pg)256 static ssize_t _mdb_read_pg(MdbHandle *mdb, unsigned char *pg_buf, unsigned long pg)
257 {
258 ssize_t len;
259 struct stat status;
260 off_t offset = pg * mdb->fmt->pg_size;
261
262 fstat(mdb->f->fd, &status);
263 if (status.st_size < offset) {
264 fprintf(stderr,"offset %lu is beyond EOF\n",offset);
265 return 0;
266 }
267 if (mdb->stats && mdb->stats->collect)
268 mdb->stats->pg_reads++;
269
270 lseek(mdb->f->fd, offset, SEEK_SET);
271 len = read(mdb->f->fd,pg_buf,mdb->fmt->pg_size);
272 if (len==-1) {
273 perror("read");
274 return 0;
275 }
276 else if (len<mdb->fmt->pg_size) {
277 /* fprintf(stderr,"EOF reached %d bytes returned.\n",len, mdb->fmt->pg_size); */
278 return 0;
279 }
280 return len;
281 }
mdb_swap_pgbuf(MdbHandle * mdb)282 void mdb_swap_pgbuf(MdbHandle *mdb)
283 {
284 char tmpbuf[MDB_PGSIZE];
285
286 memcpy(tmpbuf,mdb->pg_buf, MDB_PGSIZE);
287 memcpy(mdb->pg_buf,mdb->alt_pg_buf, MDB_PGSIZE);
288 memcpy(mdb->alt_pg_buf,tmpbuf,MDB_PGSIZE);
289 }
290
291
292 /* really stupid, just here for consistancy */
mdb_get_byte(unsigned char * buf,int offset)293 unsigned char mdb_get_byte(unsigned char *buf, int offset)
294 {
295 return buf[offset];
296 }
mdb_pg_get_byte(MdbHandle * mdb,int offset)297 unsigned char mdb_pg_get_byte(MdbHandle *mdb, int offset)
298 {
299 if (offset < 0 || offset+1 > mdb->fmt->pg_size) return -1;
300 mdb->cur_pos++;
301 return mdb->pg_buf[offset];
302 }
303
mdb_get_int16(unsigned char * buf,int offset)304 int mdb_get_int16(unsigned char *buf, int offset)
305 {
306 return buf[offset+1]*256+buf[offset];
307 }
mdb_pg_get_int16(MdbHandle * mdb,int offset)308 int mdb_pg_get_int16(MdbHandle *mdb, int offset)
309 {
310 if (offset < 0 || offset+2 > mdb->fmt->pg_size) return -1;
311 mdb->cur_pos+=2;
312 return mdb_get_int16(mdb->pg_buf, offset);
313 }
314
mdb_pg_get_int24_msb(MdbHandle * mdb,int offset)315 gint32 mdb_pg_get_int24_msb(MdbHandle *mdb, int offset)
316 {
317 gint32 l = 0;
318 if (offset <0 || offset+3 > mdb->fmt->pg_size) return -1;
319 mdb->cur_pos+=3;
320 memcpy(&l, &(mdb->pg_buf[offset]), 3);
321 return GINT32_FROM_BE(l);
322 }
mdb_get_int24(unsigned char * buf,int offset)323 gint32 mdb_get_int24(unsigned char *buf, int offset)
324 {
325 gint32 l = 0;
326 memcpy(&l, &buf[offset], 3);
327 return GINT32_FROM_LE(l);
328 }
mdb_pg_get_int24(MdbHandle * mdb,int offset)329 gint32 mdb_pg_get_int24(MdbHandle *mdb, int offset)
330 {
331 if (offset <0 || offset+3 > mdb->fmt->pg_size) return -1;
332 mdb->cur_pos+=3;
333 return mdb_get_int24(mdb->pg_buf, offset);
334 }
335
mdb_get_int32(unsigned char * buf,int offset)336 long mdb_get_int32(unsigned char *buf, int offset)
337 {
338 guint32 l;
339 memcpy(&l, &buf[offset], 4);
340 return (long)GINT32_FROM_LE(l);
341 }
mdb_pg_get_int32(MdbHandle * mdb,int offset)342 long mdb_pg_get_int32(MdbHandle *mdb, int offset)
343 {
344 if (offset <0 || offset+4 > mdb->fmt->pg_size) return -1;
345 mdb->cur_pos+=4;
346 return mdb_get_int32(mdb->pg_buf, offset);
347 }
348
mdb_get_single(unsigned char * buf,int offset)349 float mdb_get_single(unsigned char *buf, int offset)
350 {
351 union {guint32 g; float f;} f;
352 memcpy(&f, &buf[offset], 4);
353 f.g = GUINT32_FROM_LE(f.g);
354 return f.f;
355 }
mdb_pg_get_single(MdbHandle * mdb,int offset)356 float mdb_pg_get_single(MdbHandle *mdb, int offset)
357 {
358 if (offset <0 || offset+4 > mdb->fmt->pg_size) return -1;
359 mdb->cur_pos+=4;
360 return mdb_get_single(mdb->pg_buf, offset);
361 }
362
mdb_get_double(unsigned char * buf,int offset)363 double mdb_get_double(unsigned char *buf, int offset)
364 {
365 union {guint64 g; double d;} d;
366 memcpy(&d, &buf[offset], 8);
367 d.g = GUINT64_FROM_LE(d.g);
368 return d.d;
369 }
mdb_pg_get_double(MdbHandle * mdb,int offset)370 double mdb_pg_get_double(MdbHandle *mdb, int offset)
371 {
372 if (offset <0 || offset+8 > mdb->fmt->pg_size) return -1;
373 mdb->cur_pos+=8;
374 return mdb_get_double(mdb->pg_buf, offset);
375 }
376
377
378 int
mdb_set_pos(MdbHandle * mdb,int pos)379 mdb_set_pos(MdbHandle *mdb, int pos)
380 {
381 if (pos<0 || pos >= mdb->fmt->pg_size) return 0;
382
383 mdb->cur_pos=pos;
384 return pos;
385 }
mdb_get_pos(MdbHandle * mdb)386 int mdb_get_pos(MdbHandle *mdb)
387 {
388 return mdb->cur_pos;
389 }
390