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