diff --git a/src/migration/mdb/3rdparty/mdbtools/libmdb/file.c b/src/migration/mdb/3rdparty/mdbtools/libmdb/file.c index 1792e9f6c..905dd0d6b 100644 --- a/src/migration/mdb/3rdparty/mdbtools/libmdb/file.c +++ b/src/migration/mdb/3rdparty/mdbtools/libmdb/file.c @@ -1,538 +1,544 @@ /* MDB Tools - A library for reading MS Access database files * Copyright (C) 2000 Brian Bruns * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "mdbtools.h" #ifdef DMALLOC #include "dmalloc.h" #endif /* typedef struct { int pg_size; guint16 row_count_offset; guint16 tab_num_rows_offset; guint16 tab_num_cols_offset; guint16 tab_num_idxs_offset; guint16 tab_num_ridxs_offset; guint16 tab_usage_map_offset; guint16 tab_first_dpg_offset; guint16 tab_cols_start_offset; guint16 tab_ridx_entry_size; guint16 col_flags_offset; guint16 col_size_offset; guint16 col_num_offset; guint16 tab_col_entry_size; guint16 tab_free_map_offset; guint16 tab_col_offset_var; guint16 tab_col_offset_fixed; guint16 tab_row_col_num_offset; } MdbFormatConstants; */ MdbFormatConstants MdbJet4Constants = { 4096, 0x0c, 16, 45, 47, 51, 55, 56, 63, 12, 15, 23, 5, 25, 59, 7, 21, 9 }; MdbFormatConstants MdbJet3Constants = { 2048, 0x08, 12, 25, 27, 31, 35, 36, 43, 8, 13, 16, 1, 18, 39, 3, 14, 5 }; typedef struct _RC4_KEY { unsigned char state[256]; unsigned char x; unsigned char y; } RC4_KEY; #define swap_byte(x,y) t = *(x); *(x) = *(y); *(y) = t static ssize_t _mdb_read_pg(MdbHandle *mdb, void *pg_buf, unsigned long pg); static void RC4_set_key(RC4_KEY *key, int key_data_len, unsigned char *key_data_ptr) { unsigned char t; unsigned char index1; unsigned char index2; unsigned char* state; short counter; state = &key->state[0]; for(counter = 0; counter < 256; counter++) state[counter] = counter; key->x = 0; key->y = 0; index1 = 0; index2 = 0; for(counter = 0; counter < 256; counter++) { index2 = (key_data_ptr[index1] + state[counter] + index2) % 256; swap_byte(&state[counter], &state[index2]); index1 = (index1 + 1) % key_data_len; } } /* * this algorithm does 'encrypt in place' instead of inbuff/outbuff * note also: encryption and decryption use same routine * implementation supplied by (Adam Back) at */ static void RC4(RC4_KEY *key, int buffer_len, unsigned char * buff) { unsigned char t; unsigned char x; unsigned char y; unsigned char* state; unsigned char xorIndex; short counter; x = key->x; y = key->y; state = &key->state[0]; for(counter = 0; counter < buffer_len; counter++) { x = (x + 1) % 256; y = (state[x] + y) % 256; swap_byte(&state[x], &state[y]); xorIndex = (state[x] + state[y]) % 256; buff[counter] ^= state[xorIndex]; } key->x = x; key->y = y; } /** * mdb_find_file: * @filename: path to MDB (database) file * * Finds and returns the absolute path to an MDB file. Function will first try * to fstat file as passed, then search through the $MDBPATH if not found. * * Return value: gchar pointer to absolute path. Caller is responsible for * freeing. **/ static char *mdb_find_file(const char *file_name) { struct stat status; gchar *mdbpath, **dir, *tmpfname; unsigned int i = 0; /* try the provided file name first */ if (!stat(file_name, &status)) { char *result; result = g_strdup(file_name); if (!result) fprintf(stderr, "Can't alloc filename\n"); return result; } /* Now pull apart $MDBPATH and try those */ mdbpath = (gchar *) getenv("MDBPATH"); /* no path, can't find file */ if (!mdbpath || !strlen(mdbpath)) return NULL; dir = g_strsplit(mdbpath, ":", 0); while (dir[i]) { if (!strlen(dir[i])) continue; tmpfname = g_strconcat(dir[i++], "/", file_name, NULL); if (!stat(tmpfname, &status)) { g_strfreev(dir); return tmpfname; } g_free(tmpfname); } g_strfreev(dir); return NULL; } /** * mdb_set_encoding: * @mdb: Handle to MDB database file * @encoding_name: encoding name for MDB (database) file in JET3 version. * A copy of the string will be created. * * Sets encoding name for MDB (database) file in JET3 version. * JET3 databases have no usincode support but only ANSI code page (e.g. CP1252) * (not ISO), so you need to decide what code page strings in the MDB file are encoded in. * * Use this function after mdb_open()) but BEFORE any operation which reads text strings * from the MDB file. * "MDB_JET3_CHARSET" environment variable has priority over this setting. * **/ void mdb_set_encoding(MdbHandle *mdb, const char *encoding_name) { #ifdef HAVE_ICONV mdb_iconv_close(mdb); g_free(mdb->jet3_iconv_code); mdb->jet3_iconv_code = g_strdup(encoding_name); mdb_iconv_init(mdb); #endif } /** * mdb_open: * @filename: path to MDB (database) file * @flags: MDB_NOFLAGS for read-only, MDB_WRITABLE for read/write * * Opens an MDB file and returns an MdbHandle to it. MDB File may be relative * to the current directory, a full path to the file, or relative to a * component of $MDBPATH. * * Return value: pointer to MdbHandle structure. **/ MdbHandle *mdb_open(const char *filename, MdbFileFlags flags) { MdbHandle *mdb; int key[] = {0x86, 0xfb, 0xec, 0x37, 0x5d, 0x44, 0x9c, 0xfa, 0xc6, 0x5e, 0x28, 0xe6, 0x13, 0xb6}; int j, pos; int open_flags; mdb = (MdbHandle *) g_malloc0(sizeof(MdbHandle)); #if !MDB_NO_BACKENDS mdb_set_default_backend(mdb, "access"); #endif #ifdef HAVE_ICONV mdb->jet3_iconv_code = 0; mdb->iconv_in = (iconv_t)-1; mdb->iconv_out = (iconv_t)-1; #endif /* need something to bootstrap with, reassign after page 0 is read */ mdb->fmt = &MdbJet3Constants; mdb->f = (MdbFile *) g_malloc0(sizeof(MdbFile)); mdb->f->refs = 1; mdb->f->fd = -1; mdb->f->filename = mdb_find_file(filename); if (!mdb->f->filename) { fprintf(stderr, "File not found\n"); mdb_close(mdb); return NULL; } if (flags & MDB_WRITABLE) { mdb->f->writable = TRUE; open_flags = O_RDWR; } else { open_flags = O_RDONLY; } #ifdef _WIN32 open_flags |= O_BINARY; #endif mdb->f->fd = open(mdb->f->filename, open_flags); if (mdb->f->fd==-1) { fprintf(stderr,"Couldn't open file %s\n",mdb->f->filename); mdb_close(mdb); return NULL; } if (!mdb_read_pg(mdb, 0)) { fprintf(stderr,"Couldn't read first page.\n"); mdb_close(mdb); return NULL; } if (mdb->pg_buf[0] != 0) { mdb_close(mdb); return NULL; } mdb->f->jet_version = mdb_get_int32(mdb->pg_buf, 0x14); switch(mdb->f->jet_version) { case MDB_VER_JET3: mdb->fmt = &MdbJet3Constants; break; case MDB_VER_JET4: case MDB_VER_ACCDB_2007: case MDB_VER_ACCDB_2010: mdb->fmt = &MdbJet4Constants; break; default: fprintf(stderr,"Unknown Jet version.\n"); mdb_close(mdb); return NULL; } mdb->f->db_key = mdb_get_int32(mdb->pg_buf, 0x3e); /* I don't know if this value is valid for some versions? * it doesn't seem to be valid for the databases I have * * f->db_key ^= 0xe15e01b9; */ mdb->f->db_key ^= 0x4ebc8afb; /* fprintf(stderr, "Encrypted file, RC4 key seed= %d\n", mdb->f->db_key); */ if (mdb->f->db_key) { /* write is not supported for encrypted files yet */ mdb->f->writable = FALSE; /* that should be enought, but reopen the file read only just to be * sure we don't write invalid data */ close(mdb->f->fd); open_flags = O_RDONLY; #ifdef _WIN32 open_flags |= O_BINARY; #endif mdb->f->fd = open(mdb->f->filename, open_flags); if (mdb->f->fd==-1) { fprintf(stderr, "Couldn't ropen file %s in read only\n", mdb->f->filename); mdb_close(mdb); return NULL; } } /* get the db password located at 0x42 bytes into the file */ for (pos=0;pos<14;pos++) { j = mdb_get_int32(mdb->pg_buf, 0x42+pos); j ^= key[pos]; if ( j != 0) mdb->f->db_passwd[pos] = j; else mdb->f->db_passwd[pos] = '\0'; } mdb_iconv_init(mdb); return mdb; } /** * mdb_close: * @mdb: Handle to open MDB database file * * Dereferences MDB file, closes if reference count is 0, and destroys handle. * **/ void mdb_close(MdbHandle *mdb) { if (!mdb) return; mdb_free_catalog(mdb); #if !MDB_NO_STATS g_free(mdb->stats); #endif #if !MDB_NO_BACKENDS g_free(mdb->backend_name); #endif if (mdb->f) { if (mdb->f->refs > 1) { mdb->f->refs--; } else { if (mdb->f->fd != -1) close(mdb->f->fd); g_free(mdb->f->filename); g_free(mdb->f); } } mdb_iconv_close(mdb); #ifdef HAVE_ICONV g_free(mdb->jet3_iconv_code); #endif g_free(mdb); } /** * mdb_clone_handle: * @mdb: Handle to open MDB database file * * Clones an existing database handle. Cloned handle shares the file descriptor * but has its own page buffer, page position, and similar internal variables. * * Return value: new handle to the database. */ MdbHandle *mdb_clone_handle(MdbHandle *mdb) { MdbHandle *newmdb; MdbCatalogEntry *entry, *data; unsigned int i; newmdb = (MdbHandle *) g_memdup(mdb, sizeof(MdbHandle)); #if !MDB_NO_STATS newmdb->stats = NULL; #endif newmdb->catalog = g_ptr_array_new(); for (i=0;inum_catalog;i++) { entry = g_ptr_array_index(mdb->catalog,i); data = g_memdup(entry,sizeof(MdbCatalogEntry)); g_ptr_array_add(newmdb->catalog, data); } #if !MDB_NO_BACKENDS newmdb->backend_name = NULL; #endif if (mdb->f) { mdb->f->refs++; } #ifdef HAVE_ICONV newmdb->jet3_iconv_code = g_strdup(mdb->jet3_iconv_code); #endif mdb_iconv_init(newmdb); return newmdb; } /* ** mdb_read a wrapper for read that bails if anything is wrong */ ssize_t mdb_read_pg(MdbHandle *mdb, unsigned long pg) { ssize_t len; if (pg && mdb->cur_pg == pg) return mdb->fmt->pg_size; len = _mdb_read_pg(mdb, mdb->pg_buf, pg); //fprintf(stderr, "read page %d type %02x\n", pg, mdb->pg_buf[0]); mdb->cur_pg = pg; /* kan - reset the cur_pos on a new page read */ mdb->cur_pos = 0; /* kan */ return len; } ssize_t mdb_read_alt_pg(MdbHandle *mdb, unsigned long pg) { ssize_t len; len = _mdb_read_pg(mdb, mdb->alt_pg_buf, pg); return len; } static ssize_t _mdb_read_pg(MdbHandle *mdb, void *pg_buf, unsigned long pg) { ssize_t len; struct stat status; off_t offset = pg * mdb->fmt->pg_size; - fstat(mdb->f->fd, &status); + if (fstat(mdb->f->fd, &status) != 0) { + perror("fstat"); + return 0; + } if (status.st_size < offset) { fprintf(stderr,"offset %jd is beyond EOF\n",(intmax_t)offset); return 0; } #if !MDB_NO_STATS if (mdb->stats && mdb->stats->collect) mdb->stats->pg_reads++; #endif - lseek(mdb->f->fd, offset, SEEK_SET); + if (lseek(mdb->f->fd, offset, SEEK_SET) == -1) { + perror("lseek"); + return 0; + } len = read(mdb->f->fd,pg_buf,mdb->fmt->pg_size); if (len==-1) { perror("read"); return 0; } else if (lenfmt->pg_size) { /* fprintf(stderr,"EOF reached %d bytes returned.\n",len, mdb->fmt->pg_size); */ return 0; } /* * unencrypt the page if necessary. * it might make sense to cache the unencrypted data blocks? */ if (pg != 0 && mdb->f->db_key != 0) { RC4_KEY rc4_key; unsigned int tmp_key = mdb->f->db_key ^ pg; RC4_set_key(&rc4_key, 4, (unsigned char *)&tmp_key); RC4(&rc4_key, mdb->fmt->pg_size, pg_buf); } return len; } void mdb_swap_pgbuf(MdbHandle *mdb) { char tmpbuf[MDB_PGSIZE]; memcpy(tmpbuf,mdb->pg_buf, MDB_PGSIZE); memcpy(mdb->pg_buf,mdb->alt_pg_buf, MDB_PGSIZE); memcpy(mdb->alt_pg_buf,tmpbuf,MDB_PGSIZE); } unsigned char mdb_get_byte(void *buf, int offset) { return ((unsigned char *)(buf))[offset]; } unsigned char mdb_pg_get_byte(MdbHandle *mdb, int offset) { if (offset < 0 || (offset+1) > (int)mdb->fmt->pg_size) return -1; mdb->cur_pos++; return mdb->pg_buf[offset]; } int mdb_get_int16(void *buf, int offset) { guint16 l; memcpy(&l, (char*)buf + offset, 2); return (int)GUINT16_FROM_LE(l); } int mdb_pg_get_int16(MdbHandle *mdb, int offset) { if (offset < 0 || (offset+2) > (int)mdb->fmt->pg_size) return -1; mdb->cur_pos+=2; return mdb_get_int16(mdb->pg_buf, offset); } long mdb_get_int32_msb(void *buf, int offset) { gint32 l; memcpy(&l, (char*)buf + offset, 4); return (long)GINT32_FROM_BE(l); } long mdb_get_int32(void *buf, int offset) { gint32 l; memcpy(&l, (char*)buf + offset, 4); return (long)GINT32_FROM_LE(l); } long mdb_pg_get_int32(MdbHandle *mdb, int offset) { if (offset <0 || (offset+4) > (int)mdb->fmt->pg_size) return -1; mdb->cur_pos+=4; return mdb_get_int32(mdb->pg_buf, offset); } float mdb_get_single(void *buf, int offset) { union {guint32 g; float f;} f; memcpy(&f, (char*)buf + offset, 4); f.g = GUINT32_FROM_LE(f.g); return f.f; } float mdb_pg_get_single(MdbHandle *mdb, int offset) { if (offset <0 || (offset+4) > (int)mdb->fmt->pg_size) return -1; mdb->cur_pos+=4; return mdb_get_single(mdb->pg_buf, offset); } double mdb_get_double(void *buf, int offset) { union {guint64 g; double d;} d; memcpy(&d, (char*)buf + offset, 8); d.g = GUINT64_FROM_LE(d.g); return d.d; } double mdb_pg_get_double(MdbHandle *mdb, int offset) { if (offset <0 || (offset+8) > (int)mdb->fmt->pg_size) return -1; mdb->cur_pos+=8; return mdb_get_double(mdb->pg_buf, offset); } int mdb_set_pos(MdbHandle *mdb, int pos) { if (pos<0 || pos >= (int)mdb->fmt->pg_size) return 0; mdb->cur_pos=pos; return pos; } int mdb_get_pos(MdbHandle *mdb) { return mdb->cur_pos; } diff --git a/src/migration/mdb/3rdparty/mdbtools/libmdb/worktable.c b/src/migration/mdb/3rdparty/mdbtools/libmdb/worktable.c index a398d987c..b52c96b72 100644 --- a/src/migration/mdb/3rdparty/mdbtools/libmdb/worktable.c +++ b/src/migration/mdb/3rdparty/mdbtools/libmdb/worktable.c @@ -1,99 +1,105 @@ /* MDB Tools - A library for reading MS Access database files * Copyright (C) 2004 Brian Bruns * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mdbtools.h" #ifdef DMALLOC #include "dmalloc.h" #endif /* * Temp table routines. These are currently used to generate mock results for * commands like "list tables" and "describe table" */ void mdb_fill_temp_col(MdbColumn *tcol, char *col_name, int col_size, int col_type, int is_fixed) { memset(tcol,0,sizeof(MdbColumn)); + if (strlen(col_name) > MDB_MAX_OBJ_NAME) { + return; + } strcpy(tcol->name, col_name); tcol->col_type = col_type; if ((col_type == MDB_TEXT) || (col_type == MDB_MEMO)) { tcol->col_size = col_size; } else { tcol->col_size = mdb_col_fixed_size(tcol); } tcol->is_fixed = is_fixed; } void mdb_fill_temp_field(MdbField *field, void *value, int siz, int is_fixed, int is_null, int start, int colnum) { field->value = value; field->siz = siz; field->is_fixed = is_fixed; field->is_null = is_null; field->start = start; field->colnum = colnum; } MdbTableDef * mdb_create_temp_table(MdbHandle *mdb, char *name) { MdbCatalogEntry *entry; MdbTableDef *table; + if (strlen(name) > MDB_MAX_OBJ_NAME) { + return 0; + } /* dummy up a catalog entry */ entry = (MdbCatalogEntry *) g_malloc0(sizeof(MdbCatalogEntry)); entry->mdb = mdb; entry->object_type = MDB_TABLE; entry->table_pg = 0; strcpy(entry->object_name, name); table = mdb_alloc_tabledef(entry); table->columns = g_ptr_array_new(); table->is_temp_table = 1; table->temp_table_pages = g_ptr_array_new(); return table; } void mdb_temp_table_add_col(MdbTableDef *table, MdbColumn *col) { col->table = table, col->col_num = table->num_cols; if (!col->is_fixed) col->var_col_num = table->num_var_cols++; g_ptr_array_add(table->columns, g_memdup(col, sizeof(MdbColumn))); table->num_cols++; } /* * Should be called after setting up all temp table columns */ void mdb_temp_columns_end(MdbTableDef *table) { MdbColumn *col; unsigned int i; unsigned int start = 0; for (i=0; inum_cols; i++) { col = g_ptr_array_index(table->columns, i); if (col->is_fixed) { col->fixed_offset = start; start += col->col_size; } } } diff --git a/src/migration/mdb/3rdparty/mdbtools/libmdb/write.c b/src/migration/mdb/3rdparty/mdbtools/libmdb/write.c index 95b806eb7..2b742424e 100644 --- a/src/migration/mdb/3rdparty/mdbtools/libmdb/write.c +++ b/src/migration/mdb/3rdparty/mdbtools/libmdb/write.c @@ -1,882 +1,885 @@ /* MDB Tools - A library for reading MS Access database file * Copyright (C) 2000 Brian Bruns * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "mdbtools.h" #ifdef DMALLOC #include "dmalloc.h" #endif //static int mdb_copy_index_pg(MdbTableDef *table, MdbIndex *idx, MdbIndexPage *ipg); static int mdb_add_row_to_leaf_pg(MdbTableDef *table, MdbIndex *idx, MdbIndexPage *ipg, MdbField *idx_fields, guint32 pgnum, guint16 rownum); void mdb_put_int16(void *buf, guint32 offset, guint32 value) { value = GINT32_TO_LE(value); memcpy((char*)buf + offset, &value, 2); } void _mdb_put_int16(void *buf, guint32 offset, guint32 value) #ifdef HAVE_ATTRIBUTE_ALIAS __attribute__((alias("mdb_put_int16"))); #else { mdb_put_int16((char*)buf, offset, value); } #endif void mdb_put_int32(void *buf, guint32 offset, guint32 value) { value = GINT32_TO_LE(value); memcpy((char*)buf + offset, &value, 4); } void _mdb_put_int32(void *buf, guint32 offset, guint32 value) #ifdef HAVE_ATTRIBUTE_ALIAS __attribute__((alias("mdb_put_int32"))); #else { mdb_put_int32((char*)buf, offset, value); } #endif void mdb_put_int32_msb(void *buf, guint32 offset, guint32 value) { value = GINT32_TO_BE(value); memcpy((char*)buf + offset, &value, 4); } void _mdb_put_int32_mdb(void *buf, guint32 offset, guint32 value) #ifdef HAVE_ATTRIBUTE_ALIAS __attribute__((alias("mdb_put_int32_msb"))); #else { mdb_put_int32_msb((char*)buf, offset, value); } #endif ssize_t mdb_write_pg(MdbHandle *mdb, unsigned long pg) { ssize_t len; struct stat status; off_t offset = pg * mdb->fmt->pg_size; - fstat(mdb->f->fd, &status); + if (fstat(mdb->f->fd, &status) != 0) { + perror("fstat"); + return 0; + } /* is page beyond current size + 1 ? */ if ((size_t)status.st_size < (offset + mdb->fmt->pg_size)) { fprintf(stderr,"offset %jd is beyond EOF\n",(intmax_t)offset); return 0; } lseek(mdb->f->fd, offset, SEEK_SET); len = write(mdb->f->fd,mdb->pg_buf,mdb->fmt->pg_size); if (len==-1) { perror("write"); return 0; } else if (lenfmt->pg_size) { /* fprintf(stderr,"EOF reached %d bytes returned.\n",len, mdb->pg_size); */ return 0; } mdb->cur_pos = 0; return len; } static int mdb_is_col_indexed(MdbTableDef *table, int colnum) { unsigned int i, j; MdbIndex *idx; for (i=0;inum_idxs;i++) { idx = g_ptr_array_index (table->indices, i); for (j=0;jnum_keys;j++) { if (idx->key_col_num[j]==colnum) return 1; } } return 0; } static void mdb_crack_row4(MdbHandle *mdb, int row_start, int row_end, unsigned int bitmask_sz, unsigned int row_var_cols, unsigned int *var_col_offsets) { unsigned int i; for (i=0; ipg_buf, row_end - bitmask_sz - 3 - (i*2)); } } static void mdb_crack_row3(MdbHandle *mdb, int row_start, int row_end, unsigned int bitmask_sz, unsigned int row_var_cols, unsigned int *var_col_offsets) { unsigned int i; unsigned int num_jumps = 0, jumps_used = 0; unsigned int col_ptr, row_len; row_len = row_end - row_start + 1; num_jumps = (row_len - 1) / 256; col_ptr = row_end - bitmask_sz - num_jumps - 1; /* If last jump is a dummy value, ignore it */ if ((col_ptr-row_start-row_var_cols)/256 < num_jumps) num_jumps--; jumps_used = 0; for (i=0; ipg_buf[row_end-bitmask_sz-jumps_used-1])) { jumps_used++; } var_col_offsets[i] = mdb->pg_buf[col_ptr-i]+(jumps_used*256); } } /** * mdb_crack_row: * @table: Table that the row belongs to * @row_start: offset to start of row on current page * @row_end: offset to end of row on current page * @fields: pointer to MdbField array to be popluated by mdb_crack_row * * Cracks a row buffer apart into its component fields. * * A row buffer is that portion of a data page which contains the values for * that row. Its beginning and end can be found in the row offset table. * * The resulting MdbField array contains pointers into the row for each field * present. Be aware that by modifying field[]->value, you would be modifying * the row buffer itself, not a copy. * * This routine is mostly used internally by mdb_fetch_row() but may have some * applicability for advanced application programs. * * Return value: number of fields present. */ int mdb_crack_row(MdbTableDef *table, int row_start, int row_end, MdbField *fields) { MdbColumn *col; MdbCatalogEntry *entry = table->entry; MdbHandle *mdb = entry->mdb; void *pg_buf = mdb->pg_buf; unsigned int row_var_cols=0, row_cols; unsigned char *nullmask; unsigned int bitmask_sz; unsigned int *var_col_offsets = NULL; unsigned int fixed_cols_found, row_fixed_cols; unsigned int col_count_size; unsigned int i; if (mdb_get_option(MDB_DEBUG_ROW)) { mdb_buffer_dump(pg_buf, row_start, row_end - row_start + 1); } if (IS_JET3(mdb)) { row_cols = mdb_get_byte(pg_buf, row_start); col_count_size = 1; } else { row_cols = mdb_get_int16(pg_buf, row_start); col_count_size = 2; } bitmask_sz = (row_cols + 7) / 8; nullmask = (unsigned char *)pg_buf + row_end - bitmask_sz + 1; /* read table of variable column locations */ if (table->num_var_cols > 0) { row_var_cols = IS_JET3(mdb) ? mdb_get_byte(pg_buf, row_end - bitmask_sz) : mdb_get_int16(pg_buf, row_end - bitmask_sz - 1); var_col_offsets = (unsigned int *)g_malloc((row_var_cols+1)*sizeof(int)); if (IS_JET3(mdb)) { mdb_crack_row3(mdb, row_start, row_end, bitmask_sz, row_var_cols, var_col_offsets); } else { mdb_crack_row4(mdb, row_start, row_end, bitmask_sz, row_var_cols, var_col_offsets); } } fixed_cols_found = 0; row_fixed_cols = row_cols - row_var_cols; if (mdb_get_option(MDB_DEBUG_ROW)) { fprintf(stdout,"bitmask_sz %d\n", bitmask_sz); fprintf(stdout,"row_var_cols %d\n", row_var_cols); fprintf(stdout,"row_fixed_cols %d\n", row_fixed_cols); } for (i=0;inum_cols;i++) { unsigned int byte_num, bit_num; unsigned int col_start; col = g_ptr_array_index(table->columns,i); fields[i].colnum = i; fields[i].is_fixed = col->is_fixed; byte_num = col->col_num / 8; bit_num = col->col_num % 8; /* logic on nulls is reverse, 1 is not null, 0 is null */ fields[i].is_null = nullmask[byte_num] & (1 << bit_num) ? 0 : 1; if ((fields[i].is_fixed) && (fixed_cols_found < row_fixed_cols)) { col_start = col->fixed_offset + col_count_size; fields[i].start = row_start + col_start; fields[i].value = (char*)pg_buf + row_start + col_start; fields[i].siz = col->col_size; fixed_cols_found++; /* Use col->var_col_num because a deleted column is still * present in the variable column offsets table for the row */ } else if ((!fields[i].is_fixed) && (col->var_col_num < row_var_cols)) { col_start = var_col_offsets[col->var_col_num]; fields[i].start = row_start + col_start; fields[i].value = (char*)pg_buf + row_start + col_start; fields[i].siz = var_col_offsets[(col->var_col_num)+1] - col_start; } else { fields[i].start = 0; fields[i].value = NULL; fields[i].siz = 0; fields[i].is_null = 1; } } g_free(var_col_offsets); return row_cols; } static int mdb_pack_null_mask(unsigned char *buffer, int num_fields, MdbField *fields) { int pos = 0, bit = 0, byte = 0; int i; /* 'Not null' bitmap */ for (i=0; i> 8) & 0xff; /* Fixed length columns */ for (i=0;inum_var_cols == 0) { pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields); return pos; } /* Variable length columns */ for (i=0;i> 8) & 0xff; pos += 2; /* Offsets of the variable-length columns */ for (i=num_fields; i>0; i--) { if (!fields[i-1].is_fixed) { row_buffer[pos++] = fields[i-1].offset & 0xff; row_buffer[pos++] = (fields[i-1].offset >> 8) & 0xff; } } /* Number of variable-length columns */ row_buffer[pos++] = var_cols & 0xff; row_buffer[pos++] = (var_cols >> 8) & 0xff; pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields); return pos; } static int mdb_pack_row3(MdbTableDef *table, unsigned char *row_buffer, unsigned int num_fields, MdbField *fields) { unsigned int pos = 0; unsigned int var_cols = 0; unsigned int i, j; unsigned char *offset_high; row_buffer[pos++] = num_fields; /* Fixed length columns */ for (i=0;inum_var_cols == 0) { pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields); return pos; } /* Variable length columns */ for (i=0;i> 8) & 0xff; j = 1; /* EOD */ row_buffer[pos] = pos & 0xff; pos++; /* Variable length column offsets */ for (i=num_fields; i>0; i--) { if (!fields[i-1].is_fixed) { row_buffer[pos++] = fields[i-1].offset & 0xff; offset_high[j++] = (fields[i-1].offset >> 8) & 0xff; } } /* Dummy jump table entry */ if (offset_high[0] < (pos+(num_fields+7)/8-1)/255) { row_buffer[pos++] = 0xff; } /* Jump table */ for (i=0; i offset_high[i+1]) { row_buffer[pos++] = var_cols-i; } } g_free(offset_high); row_buffer[pos++] = var_cols; pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields); return pos; } int mdb_pack_row(MdbTableDef *table, unsigned char *row_buffer, int unsigned num_fields, MdbField *fields) { if (table->is_temp_table) { unsigned int i; for (i=0; icolumns, i); fields[i].is_null = (fields[i].value) ? 0 : 1; fields[i].colnum = i; fields[i].is_fixed = c->is_fixed; if ((c->col_type != MDB_TEXT) && (c->col_type != MDB_MEMO)) { fields[i].siz = c->col_size; } } } if (IS_JET3(table->entry->mdb)) { return mdb_pack_row3(table, row_buffer, num_fields, fields); } else { return mdb_pack_row4(table, row_buffer, num_fields, fields); } } int mdb_pg_get_freespace(MdbHandle *mdb) { int rows, free_start, free_end; int row_count_offset = mdb->fmt->row_count_offset; rows = mdb_get_int16(mdb->pg_buf, row_count_offset); free_start = row_count_offset + 2 + (rows * 2); free_end = mdb_get_int16(mdb->pg_buf, row_count_offset + (rows * 2)); mdb_debug(MDB_DEBUG_WRITE,"free space left on page = %d", free_end - free_start); return (free_end - free_start); } void * mdb_new_leaf_pg(MdbCatalogEntry *entry) { MdbHandle *mdb = entry->mdb; void *new_pg = g_malloc0(mdb->fmt->pg_size); mdb_put_int16(new_pg, 0, 0x0104); mdb_put_int32(new_pg, 4, entry->table_pg); return new_pg; } void * mdb_new_data_pg(MdbCatalogEntry *entry) { MdbFormatConstants *fmt = entry->mdb->fmt; void *new_pg = g_malloc0(fmt->pg_size); mdb_put_int16(new_pg, 0, 0x0101); mdb_put_int16(new_pg, 2, fmt->pg_size - fmt->row_count_offset - 2); mdb_put_int32(new_pg, 4, entry->table_pg); return new_pg; } /* could be static */ int mdb_update_indexes(MdbTableDef *table, int num_fields, MdbField *fields, guint32 pgnum, guint16 rownum) { unsigned int i; MdbIndex *idx; for (i=0;inum_idxs;i++) { idx = g_ptr_array_index (table->indices, i); mdb_debug(MDB_DEBUG_WRITE,"Updating %s (%d).", idx->name, idx->index_type); if (idx->index_type==1) { mdb_update_index(table, idx, num_fields, fields, pgnum, rownum); } } return 1; } int mdb_init_index_chain(MdbTableDef *table, MdbIndex *idx) { MdbCatalogEntry *entry = table->entry; MdbHandle *mdb = entry->mdb; table->scan_idx = idx; table->chain = g_malloc0(sizeof(MdbIndexChain)); table->mdbidx = mdb_clone_handle(mdb); mdb_read_pg(table->mdbidx, table->scan_idx->first_pg); return 1; } /* could be static */ int mdb_update_index(MdbTableDef *table, MdbIndex *idx, unsigned int num_fields, MdbField *fields, guint32 pgnum, guint16 rownum) { MdbCatalogEntry *entry = table->entry; MdbHandle *mdb = entry->mdb; /*int idx_xref[16];*/ unsigned int i, j; MdbIndexChain *chain; MdbField idx_fields[10]; for (i = 0; i < idx->num_keys; i++) { for (j = 0; j < num_fields; j++) { // key_col_num is 1 based, can't remember why though if (fields[j].colnum == idx->key_col_num[i]-1) { /* idx_xref[i] = j; */ idx_fields[i] = fields[j]; } } } /* for (i = 0; i < idx->num_keys; i++) { fprintf(stdout, "key col %d (%d) is mapped to field %d (%d %d)\n", i, idx->key_col_num[i], idx_xref[i], fields[idx_xref[i]].colnum, fields[idx_xref[i]].siz); } for (i = 0; i < num_fields; i++) { fprintf(stdout, "%d (%d %d)\n", i, fields[i].colnum, fields[i].siz); } */ chain = g_malloc0(sizeof(MdbIndexChain)); mdb_index_find_row(mdb, idx, chain, pgnum, rownum); //printf("chain depth = %d\n", chain->cur_depth); //printf("pg = %" G_GUINT32_FORMAT "\n", //chain->pages[chain->cur_depth-1].pg); //mdb_copy_index_pg(table, idx, &chain->pages[chain->cur_depth-1]); mdb_add_row_to_leaf_pg(table, idx, &chain->pages[chain->cur_depth-1], idx_fields, pgnum, rownum); return 1; } int mdb_insert_row(MdbTableDef *table, int num_fields, MdbField *fields) { int new_row_size; unsigned char row_buffer[4096]; MdbCatalogEntry *entry = table->entry; MdbHandle *mdb = entry->mdb; MdbFormatConstants *fmt = mdb->fmt; guint32 pgnum; guint16 rownum; if (!mdb->f->writable) { fprintf(stderr, "File is not open for writing\n"); return 0; } new_row_size = mdb_pack_row(table, row_buffer, num_fields, fields); if (mdb_get_option(MDB_DEBUG_WRITE)) { mdb_buffer_dump(row_buffer, 0, new_row_size); } pgnum = mdb_map_find_next_freepage(table, new_row_size); if (!pgnum) { fprintf(stderr, "Unable to allocate new page.\n"); return 0; } rownum = mdb_add_row_to_pg(table, row_buffer, new_row_size); if (mdb_get_option(MDB_DEBUG_WRITE)) { mdb_buffer_dump(mdb->pg_buf, 0, 40); mdb_buffer_dump(mdb->pg_buf, fmt->pg_size - 160, 160); } mdb_debug(MDB_DEBUG_WRITE, "writing page %d", pgnum); if (!mdb_write_pg(mdb, pgnum)) { fprintf(stderr, "write failed!\n"); return 0; } mdb_update_indexes(table, num_fields, fields, pgnum, rownum); return 1; } /* * Assumes caller has verfied space is available on page and adds the new * row to the current pg_buf. */ guint16 mdb_add_row_to_pg(MdbTableDef *table, unsigned char *row_buffer, int new_row_size) { void *new_pg; int num_rows, i, pos, row_start; size_t row_size; MdbCatalogEntry *entry = table->entry; MdbHandle *mdb = entry->mdb; MdbFormatConstants *fmt = mdb->fmt; if (table->is_temp_table) { GPtrArray *pages = table->temp_table_pages; if (pages->len == 0) { new_pg = mdb_new_data_pg(entry); g_ptr_array_add(pages, new_pg); } else { new_pg = g_ptr_array_index(pages, pages->len - 1); if (mdb_get_int16(new_pg, 2) < new_row_size + 2) { new_pg = mdb_new_data_pg(entry); g_ptr_array_add(pages, new_pg); } } num_rows = mdb_get_int16(new_pg, fmt->row_count_offset); pos = (num_rows == 0) ? fmt->pg_size : mdb_get_int16(new_pg, fmt->row_count_offset + (num_rows*2)); } else { /* is not a temp table */ new_pg = mdb_new_data_pg(entry); num_rows = mdb_get_int16(mdb->pg_buf, fmt->row_count_offset); pos = fmt->pg_size; /* copy existing rows */ for (i=0;ipg_buf + row_start, row_size); mdb_put_int16(new_pg, (fmt->row_count_offset + 2) + (i*2), pos); } } /* add our new row */ pos -= new_row_size; memcpy((char*)new_pg + pos, row_buffer, new_row_size); /* add row to the row offset table */ mdb_put_int16(new_pg, (fmt->row_count_offset + 2) + (num_rows*2), pos); /* update number rows on this page */ num_rows++; mdb_put_int16(new_pg, fmt->row_count_offset, num_rows); /* update the freespace */ mdb_put_int16(new_pg,2,pos - fmt->row_count_offset - 2 - (num_rows*2)); /* copy new page over old */ if (!table->is_temp_table) { memcpy(mdb->pg_buf, new_pg, fmt->pg_size); g_free(new_pg); } return num_rows; } int mdb_update_row(MdbTableDef *table) { int row_start, row_end; unsigned int i; MdbColumn *col; MdbCatalogEntry *entry = table->entry; MdbHandle *mdb = entry->mdb; MdbField fields[256]; unsigned char row_buffer[4096]; size_t old_row_size, new_row_size; unsigned int num_fields; if (!mdb->f->writable) { fprintf(stderr, "File is not open for writing\n"); return 0; } mdb_find_row(mdb, table->cur_row-1, &row_start, &old_row_size); row_end = row_start + old_row_size - 1; row_start &= 0x0FFF; /* remove flags */ mdb_debug(MDB_DEBUG_WRITE,"page %lu row %d start %d end %d", (unsigned long) table->cur_phys_pg, table->cur_row-1, row_start, row_end); if (mdb_get_option(MDB_DEBUG_LIKE)) mdb_buffer_dump(mdb->pg_buf, row_start, old_row_size); for (i=0;inum_cols;i++) { col = g_ptr_array_index(table->columns,i); if (col->bind_ptr && mdb_is_col_indexed(table,i)) { fprintf(stderr, "Attempting to update column that is part of an index\n"); return 0; } } num_fields = mdb_crack_row(table, row_start, row_end, fields); if (mdb_get_option(MDB_DEBUG_WRITE)) { for (i=0;inum_cols;i++) { col = g_ptr_array_index(table->columns,i); if (col->bind_ptr) { fields[i].value = col->bind_ptr; fields[i].siz = *(col->len_ptr); } } new_row_size = mdb_pack_row(table, row_buffer, num_fields, fields); if (mdb_get_option(MDB_DEBUG_WRITE)) mdb_buffer_dump(row_buffer, 0, new_row_size); if (new_row_size > (old_row_size + mdb_pg_get_freespace(mdb))) { fprintf(stderr, "No space left on this page, update will not occur\n"); return 0; } /* do it! */ mdb_replace_row(table, table->cur_row-1, row_buffer, new_row_size); return 0; /* FIXME */ } /* WARNING the return code is opposite to convention used elsewhere: * returns 0 on success * returns 1 on failure * This might change on next ABI break. */ int mdb_replace_row(MdbTableDef *table, int row, void *new_row, int new_row_size) { MdbCatalogEntry *entry = table->entry; MdbHandle *mdb = entry->mdb; int pg_size = mdb->fmt->pg_size; int rco = mdb->fmt->row_count_offset; void *new_pg; guint16 num_rows; int row_start; size_t row_size; int i, pos; if (mdb_get_option(MDB_DEBUG_WRITE)) { mdb_buffer_dump(mdb->pg_buf, 0, 40); mdb_buffer_dump(mdb->pg_buf, pg_size - 160, 160); } mdb_debug(MDB_DEBUG_WRITE,"updating row %d on page %lu", row, (unsigned long) table->cur_phys_pg); new_pg = mdb_new_data_pg(entry); num_rows = mdb_get_int16(mdb->pg_buf, rco); mdb_put_int16(new_pg, rco, num_rows); pos = pg_size; /* rows before */ for (i=0;ipg_buf + row_start, row_size); mdb_put_int16(new_pg, rco + 2 + i*2, pos); } /* our row */ pos -= new_row_size; memcpy((char*)new_pg + pos, new_row, new_row_size); mdb_put_int16(new_pg, rco + 2 + row*2, pos); /* rows after */ for (i=row+1;ipg_buf + row_start, row_size); mdb_put_int16(new_pg, rco + 2 + i*2, pos); } /* almost done, copy page over current */ memcpy(mdb->pg_buf, new_pg, pg_size); g_free(new_pg); mdb_put_int16(mdb->pg_buf, 2, mdb_pg_get_freespace(mdb)); if (mdb_get_option(MDB_DEBUG_WRITE)) { mdb_buffer_dump(mdb->pg_buf, 0, 40); mdb_buffer_dump(mdb->pg_buf, pg_size - 160, 160); } /* drum roll, please */ if (!mdb_write_pg(mdb, table->cur_phys_pg)) { fprintf(stderr, "write failed!\n"); return 1; } return 0; } static int mdb_add_row_to_leaf_pg(MdbTableDef *table, MdbIndex *idx, MdbIndexPage *ipg, MdbField *idx_fields, guint32 pgnum, guint16 rownum) /*, guint32 pgnum, guint16 rownum) static int mdb_copy_index_pg(MdbTableDef *table, MdbIndex *idx, MdbIndexPage *ipg) */ { MdbCatalogEntry *entry = table->entry; MdbHandle *mdb = entry->mdb; MdbColumn *col; guint32 pg_row; guint16 row = 0; void *new_pg; unsigned char key_hash[256]; int keycol; new_pg = mdb_new_leaf_pg(entry); /* reinitial ipg pointers to start of page */ mdb_index_page_reset(ipg); mdb_read_pg(mdb, ipg->pg); /* do we support this index type yet? */ if (idx->num_keys > 1) { fprintf(stderr,"multikey indexes not yet supported, aborting\n"); return 0; } keycol = idx->key_col_num[0]; col = g_ptr_array_index (table->columns, keycol - 1); if (!col->is_fixed) { fprintf(stderr,"variable length key columns not yet supported, aborting\n"); return 0; } while (mdb_index_find_next_on_page(mdb, ipg)) { /* check for compressed indexes. */ if (ipg->len < col->col_size + 1) { fprintf(stderr,"compressed indexes not yet supported, aborting\n"); return 0; } pg_row = mdb_get_int32_msb(mdb->pg_buf, ipg->offset + ipg->len - 4); /* guint32 pg = pg_row >> 8; */ row = pg_row & 0xff; /* unsigned char iflag = mdb->pg_buf[ipg->offset]; */ /* turn the key hash back into a value */ mdb_index_swap_n(&mdb->pg_buf[ipg->offset + 1], col->col_size, key_hash); key_hash[col->col_size - 1] &= 0x7f; if (mdb_get_option(MDB_DEBUG_WRITE)) { mdb_buffer_dump(mdb->pg_buf, ipg->offset, ipg->len); mdb_buffer_dump(mdb->pg_buf, ipg->offset + 1, col->col_size); mdb_buffer_dump(key_hash, 0, col->col_size); } memcpy((char*)new_pg + ipg->offset, mdb->pg_buf + ipg->offset, ipg->len); ipg->offset += ipg->len; ipg->len = 0; row++; } if (!row) { fprintf(stderr,"missing indexes not yet supported, aborting\n"); return 0; } //mdb_put_int16(new_pg, mdb->fmt->row_count_offset, row); /* free space left */ mdb_put_int16(new_pg, 2, mdb->fmt->pg_size - ipg->offset); //printf("offset = %d\n", ipg->offset); mdb_index_swap_n(idx_fields[0].value, col->col_size, key_hash); key_hash[0] |= 0x080; if (mdb_get_option(MDB_DEBUG_WRITE)) { printf("key_hash\n"); mdb_buffer_dump(idx_fields[0].value, 0, col->col_size); mdb_buffer_dump(key_hash, 0, col->col_size); printf("--------\n"); } ((char *)new_pg)[ipg->offset] = 0x7f; memcpy((char*)new_pg + ipg->offset + 1, key_hash, col->col_size); pg_row = (pgnum << 8) | ((rownum-1) & 0xff); mdb_put_int32_msb(new_pg, ipg->offset + 5, pg_row); ipg->idx_starts[row++] = ipg->offset + ipg->len; //ipg->idx_starts[row] = ipg->offset + ipg->len; if (mdb_get_option(MDB_DEBUG_WRITE)) { mdb_buffer_dump(mdb->pg_buf, 0, mdb->fmt->pg_size); } memcpy(mdb->pg_buf, new_pg, mdb->fmt->pg_size); mdb_index_pack_bitmap(mdb, ipg); if (mdb_get_option(MDB_DEBUG_WRITE)) { mdb_buffer_dump(mdb->pg_buf, 0, mdb->fmt->pg_size); } g_free(new_pg); return ipg->len; }