1 /* MDB Tools - A library for reading MS Access database file
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 Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18 
19 #include "mdbtools.h"
20 
21 #ifdef DMALLOC
22 #include "dmalloc.h"
23 #endif
24 
25 
mdb_col_comparer(MdbColumn ** a,MdbColumn ** b)26 static gint mdb_col_comparer(MdbColumn **a, MdbColumn **b)
27 {
28 	if ((*a)->col_num > (*b)->col_num)
29 		return 1;
30 	else if ((*a)->col_num < (*b)->col_num)
31 		return -1;
32 	else
33 		return 0;
34 }
35 
mdb_col_needs_size(int col_type)36 unsigned char mdb_col_needs_size(int col_type)
37 {
38 	if (col_type == MDB_TEXT) {
39 		return TRUE;
40 	} else {
41 		return FALSE;
42 	}
43 }
44 
mdb_alloc_tabledef(MdbCatalogEntry * entry)45 MdbTableDef *mdb_alloc_tabledef(MdbCatalogEntry *entry)
46 {
47 	MdbTableDef *table;
48 
49 	table = (MdbTableDef *) g_malloc0(sizeof(MdbTableDef));
50 	table->entry=entry;
51 	strcpy(table->name, entry->object_name);
52 
53 	return table;
54 }
mdb_free_tabledef(MdbTableDef * table)55 void mdb_free_tabledef(MdbTableDef *table)
56 {
57 	if (!table) return;
58 	if (table->is_temp_table) {
59 		unsigned int i;
60 		/* Temp table pages are being stored in memory */
61 		for (i=0; i<table->temp_table_pages->len; i++)
62 			g_free(g_ptr_array_index(table->temp_table_pages,i));
63 		g_ptr_array_free(table->temp_table_pages, TRUE);
64 		/* Temp tables use dummy entries */
65 		g_free(table->entry);
66 	}
67 	mdb_free_columns(table->columns);
68 	mdb_free_indices(table->indices);
69 	g_free(table->usage_map);
70 	g_free(table->free_usage_map);
71 	g_free(table);
72 }
mdb_read_table(MdbCatalogEntry * entry)73 MdbTableDef *mdb_read_table(MdbCatalogEntry *entry)
74 {
75 	MdbTableDef *table;
76 	MdbHandle *mdb = entry->mdb;
77 	MdbFormatConstants *fmt = mdb->fmt;
78 	int row_start, pg_row;
79 	char *buf, *pg_buf = (char *)mdb->pg_buf;
80 	guint i;
81 
82 	mdb_read_pg(mdb, entry->table_pg);
83 	if (mdb_get_byte(pg_buf, 0) != 0x02)  /* not a valid table def page */
84 		return NULL;
85 	table = mdb_alloc_tabledef(entry);
86 
87 	mdb_get_int16(pg_buf, 8); /* len */
88 
89 	table->num_rows = mdb_get_int32(pg_buf, fmt->tab_num_rows_offset);
90 	table->num_var_cols = mdb_get_int16(pg_buf, fmt->tab_num_cols_offset-2);
91 	table->num_cols = mdb_get_int16(pg_buf, fmt->tab_num_cols_offset);
92 	table->num_idxs = mdb_get_int32(pg_buf, fmt->tab_num_idxs_offset);
93 	table->num_real_idxs = mdb_get_int32(pg_buf, fmt->tab_num_ridxs_offset);
94 
95 	/* grab a copy of the usage map */
96 	pg_row = mdb_get_int32(pg_buf, fmt->tab_usage_map_offset);
97 	mdb_find_pg_row(mdb, pg_row, &buf, &row_start, &(table->map_sz));
98 	table->usage_map = g_memdup((char*)buf + row_start, (int)table->map_sz);
99 	if (mdb_get_option(MDB_DEBUG_USAGE))
100 		mdb_buffer_dump(buf, row_start, table->map_sz);
101 	mdb_debug(MDB_DEBUG_USAGE,"usage map found on page %ld row %d start %d len %d",
102 		pg_row >> 8, pg_row & 0xff, row_start, table->map_sz);
103 
104 	/* grab a copy of the free space page map */
105 	pg_row = mdb_get_int32(pg_buf, fmt->tab_free_map_offset);
106 	mdb_find_pg_row(mdb, pg_row, &buf, &row_start, &(table->freemap_sz));
107 	table->free_usage_map = g_memdup((char*)buf + row_start, (int)table->freemap_sz);
108 	mdb_debug(MDB_DEBUG_USAGE,"free map found on page %ld row %d start %d len %d\n",
109 		pg_row >> 8, pg_row & 0xff, row_start, table->freemap_sz);
110 
111 	table->first_data_pg = mdb_get_int16(pg_buf, fmt->tab_first_dpg_offset);
112 
113 	if (entry->props)
114 		for (i=0; i<entry->props->len; ++i) {
115 			MdbProperties *props = g_array_index(entry->props, MdbProperties*, i);
116 			if (!props->name)
117 				table->props = props;
118 		}
119 
120 	return table;
121 }
mdb_read_table_by_name(MdbHandle * mdb,gchar * table_name,int obj_type)122 MdbTableDef *mdb_read_table_by_name(MdbHandle *mdb, gchar *table_name, int obj_type)
123 {
124 	unsigned int i;
125 	MdbCatalogEntry *entry;
126 
127 	mdb_read_catalog(mdb, obj_type);
128 
129 	for (i=0; i<mdb->num_catalog; i++) {
130 		entry = g_ptr_array_index(mdb->catalog, i);
131 		if (!g_ascii_strcasecmp(entry->object_name, table_name))
132 			return mdb_read_table(entry);
133 	}
134 
135 	return NULL;
136 }
137 
138 
139 guint32
read_pg_if_32(MdbHandle * mdb,int * cur_pos)140 read_pg_if_32(MdbHandle *mdb, int *cur_pos)
141 {
142 	char c[4];
143 
144 	read_pg_if_n(mdb, c, cur_pos, 4);
145 	return mdb_get_int32(c, 0);
146 }
147 guint16
read_pg_if_16(MdbHandle * mdb,int * cur_pos)148 read_pg_if_16(MdbHandle *mdb, int *cur_pos)
149 {
150 	char c[2];
151 
152 	read_pg_if_n(mdb, c, cur_pos, 2);
153 	return mdb_get_int16(c, 0);
154 }
155 guint8
read_pg_if_8(MdbHandle * mdb,int * cur_pos)156 read_pg_if_8(MdbHandle *mdb, int *cur_pos)
157 {
158 	guint8 c;
159 
160 	read_pg_if_n(mdb, (char *)&c, cur_pos, 1);
161 	return c;
162 }
163 /*
164  * Read data into a buffer, advancing pages and setting the
165  * page cursor as needed.  In the case that buf in NULL, pages
166  * are still advanced and the page cursor is still updated.
167  */
168 void *
read_pg_if_n(MdbHandle * mdb,char * buf,int * cur_pos,size_t len)169 read_pg_if_n(MdbHandle *mdb, char *buf, int *cur_pos, size_t len)
170 {
171 	char* _buf = buf;
172 	/* Advance to page which contains the first byte */
173 	while (*cur_pos >= mdb->fmt->pg_size) {
174 		mdb_read_pg(mdb, mdb_get_int32((char *)mdb->pg_buf,4));
175 		*cur_pos -= (int)(mdb->fmt->pg_size - 8);
176 	}
177 	/* Copy pages into buffer */
178 	while ((ssize_t)(*cur_pos + len) >= mdb->fmt->pg_size) {
179 		int piece_len = mdb->fmt->pg_size - *cur_pos;
180 		if (_buf) {
181 			memcpy(_buf, mdb->pg_buf + *cur_pos, piece_len);
182 			_buf += piece_len;
183 		}
184 		len -= piece_len;
185 		mdb_read_pg(mdb, mdb_get_int32((char *)mdb->pg_buf,4));
186 		*cur_pos = 8;
187 	}
188 	/* Copy into buffer from final page */
189 	if (len && _buf) {
190 		memcpy(_buf, mdb->pg_buf + *cur_pos, len);
191 	}
192 	*cur_pos += len;
193 	return _buf;
194 }
195 
196 
mdb_append_column(GPtrArray * columns,MdbColumn * in_col)197 void mdb_append_column(GPtrArray *columns, MdbColumn *in_col)
198 {
199 	g_ptr_array_add(columns, g_memdup(in_col,sizeof(MdbColumn)));
200 }
mdb_free_columns(GPtrArray * columns)201 void mdb_free_columns(GPtrArray *columns)
202 {
203 	unsigned int i, j;
204 	MdbColumn *col;
205 
206 	if (!columns) return;
207 	for (i=0; i<columns->len; i++) {
208 		col = (MdbColumn *) g_ptr_array_index(columns, i);
209 		if (col->sargs) {
210 			for (j=0; j<col->sargs->len; j++) {
211 				g_free( g_ptr_array_index(col->sargs, j));
212 			}
213 			g_ptr_array_free(col->sargs, TRUE);
214 		}
215 		g_free(col);
216 	}
217 	g_ptr_array_free(columns, TRUE);
218 }
mdb_read_columns(MdbTableDef * table)219 GPtrArray *mdb_read_columns(MdbTableDef *table)
220 {
221 	MdbHandle *mdb = table->entry->mdb;
222 	MdbFormatConstants *fmt = mdb->fmt;
223 	MdbColumn *pcol;
224 	unsigned char *col;
225 	unsigned int i, j;
226 	int cur_pos;
227 	size_t name_sz;
228 	GArray *allprops;
229 
230 	table->columns = g_ptr_array_new();
231 
232 	col = (unsigned char *) g_malloc(fmt->tab_col_entry_size);
233 
234 	cur_pos = fmt->tab_cols_start_offset +
235 		(table->num_real_idxs * fmt->tab_ridx_entry_size);
236 
237 	/* new code based on patch submitted by Tim Nelson 2000.09.27 */
238 
239 	/*
240 	** column attributes
241 	*/
242 	for (i=0;i<table->num_cols;i++) {
243 #ifdef MDB_DEBUG
244 	/* printf("column %d\n", i);
245 	mdb_buffer_dump(mdb->pg_buf, cur_pos, fmt->tab_col_entry_size); */
246 #endif
247 		read_pg_if_n(mdb, (char *)col, &cur_pos, fmt->tab_col_entry_size);
248 		pcol = (MdbColumn *) g_malloc0(sizeof(MdbColumn));
249 
250 		pcol->table = table;
251 
252 		pcol->col_type = col[0];
253 
254 		// col_num_offset == 1 or 5
255 		pcol->col_num = col[fmt->col_num_offset];
256 
257 		//fprintf(stdout,"----- column %d -----\n",pcol->col_num);
258 		// col_var == 3 or 7
259 		pcol->var_col_num = mdb_get_int16((char *)col, fmt->tab_col_offset_var);
260 		//fprintf(stdout,"var column pos %d\n",pcol->var_col_num);
261 
262 		// col_var == 5 or 9
263 		pcol->row_col_num = mdb_get_int16((char *)col, fmt->tab_row_col_num_offset);
264 		//fprintf(stdout,"row column num %d\n",pcol->row_col_num);
265 
266 		/* FIXME: can this be right in Jet3 and Jet4? */
267 		if (pcol->col_type == MDB_NUMERIC) {
268 			pcol->col_prec = col[11];
269 			pcol->col_scale = col[12];
270 		}
271 
272 		// col_flags_offset == 13 or 15
273 		pcol->is_fixed = col[fmt->col_flags_offset] & 0x01 ? 1 : 0;
274 		pcol->is_long_auto = col[fmt->col_flags_offset] & 0x04 ? 1 : 0;
275 		pcol->is_uuid_auto = col[fmt->col_flags_offset] & 0x40 ? 1 : 0;
276 
277 		// tab_col_offset_fixed == 14 or 21
278 		pcol->fixed_offset = mdb_get_int16((char *)col, fmt->tab_col_offset_fixed);
279 		//fprintf(stdout,"fixed column offset %d\n",pcol->fixed_offset);
280 		//fprintf(stdout,"col type %s\n",pcol->is_fixed ? "fixed" : "variable");
281 
282 		if (pcol->col_type != MDB_BOOL) {
283 			// col_size_offset == 16 or 23
284 			pcol->col_size = mdb_get_int16((char *)col, fmt->col_size_offset);
285 		} else {
286 			pcol->col_size=0;
287 		}
288 
289 		g_ptr_array_add(table->columns, pcol);
290 	}
291 
292 	g_free (col);
293 
294 	/*
295 	** column names - ordered the same as the column attributes table
296 	*/
297 	for (i=0;i<table->num_cols;i++) {
298 		char *tmp_buf;
299 		pcol = g_ptr_array_index(table->columns, i);
300 
301 		if (IS_JET3(mdb))
302 			name_sz = read_pg_if_8(mdb, &cur_pos);
303 		else
304 			name_sz = read_pg_if_16(mdb, &cur_pos);
305 		tmp_buf = (char *) g_malloc(name_sz);
306 		read_pg_if_n(mdb, tmp_buf, &cur_pos, name_sz);
307 		mdb_unicode2ascii(mdb, tmp_buf, name_sz, pcol->name, MDB_MAX_OBJ_NAME);
308 		g_free(tmp_buf);
309 
310 
311 	}
312 
313 	/* Sort the columns by col_num */
314 	g_ptr_array_sort(table->columns, (GCompareFunc)mdb_col_comparer);
315 
316 	allprops = table->entry->props;
317 	if (allprops)
318 		for (i=0;i<table->num_cols;i++) {
319 			pcol = g_ptr_array_index(table->columns, i);
320 			for (j=0; j<allprops->len; ++j) {
321 				MdbProperties *props = g_array_index(allprops, MdbProperties*, j);
322 				if (props->name && !strcmp(props->name, pcol->name)) {
323 					pcol->props = props;
324 					break;
325 				}
326 
327 			}
328 		}
329 	table->index_start = cur_pos;
330 	return table->columns;
331 }
332 
333 #if !MDB_NO_BACKENDS
mdb_table_dump(MdbCatalogEntry * entry)334 void mdb_table_dump(MdbCatalogEntry *entry)
335 {
336 MdbTableDef *table;
337 MdbColumn *col;
338 int coln;
339 MdbIndex *idx;
340 unsigned int i, bitn;
341 guint32 pgnum;
342 
343 	table = mdb_read_table(entry);
344 	fprintf(stdout,"definition page     = %lu\n",entry->table_pg);
345 	fprintf(stdout,"number of datarows  = %d\n",table->num_rows);
346 	fprintf(stdout,"number of columns   = %d\n",table->num_cols);
347 	fprintf(stdout,"number of indices   = %d\n",table->num_real_idxs);
348 
349 	if (table->props)
350 		mdb_dump_props(table->props, stdout, 0);
351 	mdb_read_columns(table);
352 	mdb_read_indices(table);
353 
354 	for (i=0;i<table->num_cols;i++) {
355 		col = g_ptr_array_index(table->columns,i);
356 
357 		fprintf(stdout,"column %d Name: %-20s Type: %s(%d)\n",
358 			i, col->name,
359 			mdb_get_colbacktype_string(col),
360 			col->col_size);
361 		if (col->props)
362 			mdb_dump_props(col->props, stdout, 0);
363 	}
364 
365 	for (i=0;i<table->num_idxs;i++) {
366 		idx = g_ptr_array_index (table->indices, i);
367 		mdb_index_dump(table, idx);
368 	}
369 	if (table->usage_map) {
370 		printf("pages reserved by this object\n");
371 		printf("usage map pg %" G_GUINT32_FORMAT "\n",
372 			table->map_base_pg);
373 		printf("free map pg %" G_GUINT32_FORMAT "\n",
374 			table->freemap_base_pg);
375 		pgnum = mdb_get_int32(table->usage_map,1);
376 		/* the first 5 bytes of the usage map mean something */
377 		coln = 0;
378 		for (i=5;i<table->map_sz;i++) {
379 			for (bitn=0;bitn<8;bitn++) {
380 				if (table->usage_map[i] & 1 << bitn) {
381 					coln++;
382 					printf("%6" G_GUINT32_FORMAT, pgnum);
383 					if (coln==10) {
384 						printf("\n");
385 						coln = 0;
386 					} else {
387 						printf(" ");
388 					}
389 				}
390 				pgnum++;
391 			}
392 		}
393 		printf("\n");
394 	}
395 }
396 #endif
397 
mdb_is_user_table(MdbCatalogEntry * entry)398 int mdb_is_user_table(MdbCatalogEntry *entry)
399 {
400 	return ((entry->object_type == MDB_TABLE)
401 	 && !(entry->flags & 0x80000002)) ? 1 : 0;
402 }
mdb_is_system_table(MdbCatalogEntry * entry)403 int mdb_is_system_table(MdbCatalogEntry *entry)
404 {
405 	return ((entry->object_type == MDB_TABLE)
406 	 && (entry->flags & 0x80000002)) ? 1 : 0;
407 }
408 
409 const char *
mdb_table_get_prop(const MdbTableDef * table,const gchar * key)410 mdb_table_get_prop(const MdbTableDef *table, const gchar *key) {
411 	if (!table->props)
412 		return NULL;
413 	return g_hash_table_lookup(table->props->hash, key);
414 }
415 
416 const char *
mdb_col_get_prop(const MdbColumn * col,const gchar * key)417 mdb_col_get_prop(const MdbColumn *col, const gchar *key) {
418 	if (!col->props)
419 		return NULL;
420 	return g_hash_table_lookup(col->props->hash, key);
421 }
422