
Upgrading to Ubuntu 18.04, which seems to have become more prone to allocating process memory above the 4G boundary, has revealed that the client code of the phony msi.dll functions is not in fact prepared to accept arbitrary machine-word sized pointer values. It's expecting 32-bit handles, so we were segfaulting because MsiDatabaseImportW was only being given the bottom 32 bits of the 64-bit pointer that MsiOpenDatabaseW had returned. So now I just keep a trivial registry of small integer handle values and the pointers they map to. I don't even bother to recycle them - the process isn't expected to run for long enough for me to care.
279 lines
7.7 KiB
C
279 lines
7.7 KiB
C
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
#include <uchar.h>
|
|
|
|
#include <err.h>
|
|
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include "memory.h"
|
|
#include "dupstr.h"
|
|
#include "subproc.h"
|
|
#include "uchars.h"
|
|
|
|
/*
|
|
* The same routine MsiCloseHandle is used by our client to dispose of
|
|
* three kinds of structure that _we_ think of as completely different
|
|
* types. So we must ensure all three start with distinct magic number
|
|
* fields, so as to know what kind we're being asked to close.
|
|
*/
|
|
typedef struct MsiTypePrefix {
|
|
enum {
|
|
MAIN = 0x2B7FB8B8,
|
|
VIEW = 0x1570B0E3,
|
|
RECORD = 0x62365065
|
|
} type;
|
|
} MsiTypePrefix;
|
|
|
|
typedef struct MsiMainCtx {
|
|
MsiTypePrefix t;
|
|
|
|
char *tempdir;
|
|
char *outfile;
|
|
|
|
char **args;
|
|
int nargs, argsize;
|
|
} MsiMainCtx;
|
|
|
|
/*
|
|
* The Msi functions will expect to identify things by 32-bit handles,
|
|
* not machine-word sized pointers. So we must keep a list of handles
|
|
* we've allocated.
|
|
*/
|
|
typedef uint32_t MsiHandle;
|
|
static void **msi_handles = NULL;
|
|
static size_t n_msi_handles = 0, msi_handles_size = 0;
|
|
|
|
static MsiHandle make_handle(void *ptr)
|
|
{
|
|
size_t index;
|
|
|
|
if (n_msi_handles >= msi_handles_size) {
|
|
msi_handles_size = n_msi_handles * 5 / 4 + 512;
|
|
msi_handles = sresize(msi_handles, msi_handles_size, void *);
|
|
}
|
|
|
|
index = n_msi_handles++;
|
|
msi_handles[index] = ptr;
|
|
|
|
/*
|
|
* A mild error-correcting code, to ensure our handles make sense.
|
|
*/
|
|
return index * 59 + 17;
|
|
}
|
|
|
|
static void *lookup_handle(MsiHandle h)
|
|
{
|
|
size_t index;
|
|
|
|
assert(h % 59 == 17);
|
|
index = h / 59;
|
|
|
|
assert(index < n_msi_handles);
|
|
return msi_handles[index];
|
|
}
|
|
|
|
static MsiMainCtx *lookup_handle_main(MsiHandle handle)
|
|
{
|
|
MsiMainCtx *toret = lookup_handle(handle);
|
|
assert(toret->t.type == MAIN);
|
|
return toret;
|
|
}
|
|
|
|
uint32_t MsiOpenDatabaseW(const char16_t *filename,
|
|
const char16_t *persist,
|
|
MsiHandle *out_handle)
|
|
{
|
|
MsiMainCtx *ctx = snew(MsiMainCtx);
|
|
ctx->t.type = MAIN;
|
|
ctx->outfile = ascii(filename, true);
|
|
close(open(ctx->outfile, O_CREAT | O_WRONLY, 0666));
|
|
ctx->outfile = realpath(ctx->outfile, NULL);
|
|
unlink(ctx->outfile);
|
|
ctx->tempdir = dupcat(ctx->outfile, "-msiXXXXXX", cNULL);
|
|
if (!mkdtemp(ctx->tempdir))
|
|
err(1, "%s: mkdtemp", ctx->tempdir);
|
|
ctx->nargs = 0;
|
|
ctx->argsize = 16;
|
|
ctx->args = snewn(ctx->argsize, char *);
|
|
ctx->args[ctx->nargs++] = dupstr("sh");
|
|
ctx->args[ctx->nargs++] = dupstr("-c");
|
|
ctx->args[ctx->nargs++] = dupstr("cd \"$0\" && \"$@\"");
|
|
ctx->args[ctx->nargs++] = dupstr(ctx->tempdir);
|
|
ctx->args[ctx->nargs++] = dupstr("msibuild");
|
|
ctx->args[ctx->nargs++] = dupstr(ctx->outfile);
|
|
*out_handle = make_handle(ctx);
|
|
return 0;
|
|
}
|
|
|
|
uint32_t MsiDatabaseImportW(MsiHandle handle, const char16_t *folder,
|
|
const char16_t *file)
|
|
{
|
|
MsiMainCtx *ctx = lookup_handle_main(handle);
|
|
system_argv("sh", "-c", "cd \"$0\" && cp \"$1\" \"$2\"",
|
|
ascii(folder, true), ascii(file, true), ctx->tempdir, cNULL);
|
|
if (ctx->nargs + 2 >= ctx->argsize) {
|
|
ctx->argsize = ctx->nargs * 5 / 4 + 16;
|
|
ctx->args = sresize(ctx->args, ctx->argsize, char *);
|
|
}
|
|
ctx->args[ctx->nargs++] = dupstr("-i");
|
|
ctx->args[ctx->nargs++] = dupcat(ctx->tempdir, "/", ascii(file, true),
|
|
cNULL);
|
|
return 0;
|
|
}
|
|
|
|
typedef struct MsiView {
|
|
MsiTypePrefix t;
|
|
|
|
FILE *fp;
|
|
char *targetdir;
|
|
MsiMainCtx *ctx;
|
|
} MsiView;
|
|
|
|
static MsiView *lookup_handle_view(MsiHandle handle)
|
|
{
|
|
MsiView *toret = lookup_handle(handle);
|
|
assert(toret->t.type == VIEW);
|
|
return toret;
|
|
}
|
|
|
|
uint32_t MsiDatabaseOpenViewW(MsiHandle handle, const char16_t *query,
|
|
MsiHandle *outhandle)
|
|
{
|
|
MsiMainCtx *ctx = lookup_handle_main(handle);
|
|
MsiView *view = snew(MsiView);
|
|
view->t.type = VIEW;
|
|
view->ctx = ctx;
|
|
char *cquery = ascii(query, false);
|
|
if (!strcmp(cquery, "SELECT `Name`, `Data` FROM `_Streams`"))
|
|
view->fp = NULL; /* special case */
|
|
else {
|
|
if (!strcmp(cquery, "SELECT `Name`, `Data` FROM `Binary`")) {
|
|
view->fp = fopen(dupcat(ctx->tempdir, "/", "Binary.idt", cNULL),
|
|
"a");
|
|
view->targetdir = dupcat(ctx->tempdir, "/", "Binary", cNULL);
|
|
} else if (!strcmp(cquery, "SELECT `Name`, `Data` FROM `Icon`")) {
|
|
view->fp = fopen(dupcat(ctx->tempdir, "/", "Icon.idt", cNULL),
|
|
"a");
|
|
view->targetdir = dupcat(ctx->tempdir, "/", "Icon", cNULL);
|
|
} else
|
|
errx(1, "unrecognised query: %s", cquery);
|
|
if (!view->fp)
|
|
err(1, "open");
|
|
if (mkdir(view->targetdir, 0777) < 0)
|
|
err(1, "%s: mkdir", view->targetdir);
|
|
}
|
|
*outhandle = make_handle(view);
|
|
return 0;
|
|
}
|
|
|
|
uint32_t MsiViewExecute(MsiHandle handle, void *params)
|
|
{
|
|
lookup_handle_view(handle);
|
|
return 0;
|
|
}
|
|
|
|
typedef struct MsiRecord {
|
|
MsiTypePrefix t;
|
|
|
|
char *name, *data;
|
|
} MsiRecord;
|
|
|
|
static MsiRecord *lookup_handle_record(MsiHandle handle)
|
|
{
|
|
MsiRecord *toret = lookup_handle(handle);
|
|
assert(toret->t.type == RECORD);
|
|
return toret;
|
|
}
|
|
|
|
MsiHandle MsiCreateRecord(uint32_t nparams)
|
|
{
|
|
MsiRecord *rec = snew(MsiRecord);
|
|
rec->t.type = RECORD;
|
|
|
|
if (nparams != 2)
|
|
errx(1, "bad MsiCreateRecord param count %u", (unsigned)nparams);
|
|
rec->name = rec->data = NULL;
|
|
return make_handle(rec);
|
|
}
|
|
|
|
uint32_t MsiRecordSetStringW(MsiHandle handle, uint32_t field, char16_t *value)
|
|
{
|
|
MsiRecord *rec = lookup_handle_record(handle);
|
|
if (field != 1)
|
|
errx(1, "bad MsiRecordSetString param index %u", (unsigned)field);
|
|
rec->name = ascii(value, false);
|
|
return 0;
|
|
}
|
|
|
|
uint32_t MsiRecordSetStreamW(MsiHandle handle, uint32_t field, char16_t *path)
|
|
{
|
|
MsiRecord *rec = lookup_handle_record(handle);
|
|
if (field != 2)
|
|
errx(1, "bad MsiRecordSetStream param index %u", (unsigned)field);
|
|
rec->data = ascii(path, true);
|
|
return 0;
|
|
}
|
|
|
|
uint32_t MsiViewModify(MsiHandle viewhandle, uint32_t mode, MsiHandle rechandle)
|
|
{
|
|
MsiView *view = lookup_handle_view(viewhandle);
|
|
MsiRecord *rec = lookup_handle_record(rechandle);
|
|
if (view->fp) {
|
|
system_argv("sh", "-c", "cp \"$0\" \"$1\"/\"$2\"",
|
|
rec->data, view->targetdir, rec->name, cNULL);
|
|
fprintf(view->fp, "%s\t%s\r\n", rec->name, rec->name);
|
|
} else {
|
|
MsiMainCtx *ctx = view->ctx;
|
|
if (ctx->nargs + 3 >= ctx->argsize) {
|
|
ctx->argsize = ctx->nargs * 5 / 4 + 16;
|
|
ctx->args = sresize(ctx->args, ctx->argsize, char *);
|
|
}
|
|
ctx->args[ctx->nargs++] = dupstr("-a");
|
|
ctx->args[ctx->nargs++] = dupstr(rec->name);
|
|
ctx->args[ctx->nargs++] = dupstr(rec->data);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
uint32_t MsiCloseHandle(MsiHandle handle)
|
|
{
|
|
MsiTypePrefix *t = lookup_handle(handle);
|
|
if (t->type == VIEW) {
|
|
MsiView *view = (MsiView *)t;
|
|
if (view->fp)
|
|
fclose(view->fp);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
uint32_t MsiDatabaseCommit(MsiHandle handle)
|
|
{
|
|
MsiMainCtx *ctx = lookup_handle_main(handle);
|
|
printf("commit:");
|
|
for (int i = 0; i < ctx->nargs; i++) {
|
|
printf(" '");
|
|
for (const char *p = ctx->args[i]; *p; p++) {
|
|
if (*p == '\'')
|
|
printf("'\\''");
|
|
else
|
|
putchar(*p);
|
|
}
|
|
printf("'");
|
|
}
|
|
printf("\n");
|
|
|
|
if (ctx->nargs + 1 >= ctx->argsize) {
|
|
ctx->argsize = ctx->nargs * 5 / 4 + 16;
|
|
ctx->args = sresize(ctx->args, ctx->argsize, char *);
|
|
}
|
|
ctx->args[ctx->nargs++] = NULL;
|
|
system_argv_array(ctx->args);
|
|
return 0;
|
|
}
|