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 
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  **/
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
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  */
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 */
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 }
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 }
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 }
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 */
293 unsigned char mdb_get_byte(unsigned char *buf, int offset)
294 {
295 	return buf[offset];
296 }
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 
304 int mdb_get_int16(unsigned char *buf, int offset)
305 {
306 	return buf[offset+1]*256+buf[offset];
307 }
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 
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 }
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 }
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 
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 }
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 
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 }
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 
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 }
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
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 }
386 int mdb_get_pos(MdbHandle *mdb)
387 {
388 	return mdb->cur_pos;
389 }
390