diff options
author | Simon Tatham <anakin@pobox.com> | 2017-05-16 19:06:37 +0100 |
---|---|---|
committer | Simon Tatham <anakin@pobox.com> | 2017-05-16 19:06:37 +0100 |
commit | af3d986af771c8ce6401df5dd69504fb350c7de9 (patch) | |
tree | e1ab5557a1308d82fafaec1f925d89549a686c0d | |
parent | f0cdcb370ec7023428bc421e3a8b5f5cbf1de502 (diff) | |
download | wix-on-linux-af3d986af771c8ce6401df5dd69504fb350c7de9.tar.gz wix-on-linux-af3d986af771c8ce6401df5dd69504fb350c7de9.tar.bz2 wix-on-linux-af3d986af771c8ce6401df5dd69504fb350c7de9.zip |
Implement MsiGetFileVersion.
This is pretty ugly code, but it works well enough to deliver the
right versions for the files in my test MSI. I can polish it later.
-rw-r--r-- | fake-lib.c | 12 | ||||
-rw-r--r-- | fake-lib.h | 1 | ||||
-rw-r--r-- | fake-msi.c | 181 |
3 files changed, 188 insertions, 6 deletions
@@ -128,3 +128,15 @@ char *dupcat(const char *str, ...) | |||
128 | 128 | ||
129 | return out; | 129 | return out; |
130 | } | 130 | } |
131 | |||
132 | unsigned le(const unsigned char *buf, size_t len, size_t off, size_t nbytes) | ||
133 | { | ||
134 | unsigned toret = 0; | ||
135 | off += nbytes; | ||
136 | while (nbytes-- > 0) { | ||
137 | toret <<= 8; | ||
138 | if (--off < len) | ||
139 | toret |= buf[off]; | ||
140 | } | ||
141 | return toret; | ||
142 | } | ||
@@ -5,6 +5,7 @@ void c16cpy(char16_t *out, uint32_t *outsize, char *s); | |||
5 | void *smalloc(size_t size); | 5 | void *smalloc(size_t size); |
6 | void *srealloc(void *ptr, size_t size); | 6 | void *srealloc(void *ptr, size_t size); |
7 | char *dupcat(const char *str, ...); | 7 | char *dupcat(const char *str, ...); |
8 | unsigned le(const unsigned char *buf, size_t len, size_t off, size_t nbytes); | ||
8 | 9 | ||
9 | #define snew(type) ((type *)smalloc(sizeof(type))) | 10 | #define snew(type) ((type *)smalloc(sizeof(type))) |
10 | #define snewn(n,type) ((type *)smalloc((n)*sizeof(type))) | 11 | #define snewn(n,type) ((type *)smalloc((n)*sizeof(type))) |
@@ -9,6 +9,10 @@ | |||
9 | #include <err.h> | 9 | #include <err.h> |
10 | 10 | ||
11 | #include <fcntl.h> | 11 | #include <fcntl.h> |
12 | #include <sys/mman.h> | ||
13 | #include <sys/types.h> | ||
14 | #include <sys/stat.h> | ||
15 | #include <unistd.h> | ||
12 | 16 | ||
13 | #include "fake-lib.h" | 17 | #include "fake-lib.h" |
14 | 18 | ||
@@ -16,14 +20,179 @@ uint32_t MsiGetFileVersionW(const char16_t *filename, | |||
16 | char16_t *version, uint32_t *version_size, | 20 | char16_t *version, uint32_t *version_size, |
17 | char16_t *language, uint32_t *language_size) | 21 | char16_t *language, uint32_t *language_size) |
18 | { | 22 | { |
19 | warnx("FIXME: MsiGetFileVersion(%s)", ascii(filename, true)); | 23 | char *fname = ascii(filename, true); |
20 | if (version) { | 24 | uint32_t toret = 1006; /* ERROR_FILE_INVALID == 'no version info found' */ |
21 | c16cpy(version, version_size, "0.1.2.3"); | 25 | int fd = -1; |
26 | void *mapv = MAP_FAILED; | ||
27 | |||
28 | fd = open(fname, O_RDONLY); | ||
29 | if (fd < 0) | ||
30 | err(1, "%s: open", fname); | ||
31 | struct stat st; | ||
32 | if (fstat(fd, &st) < 0) | ||
33 | err(1, "%s: fstat", fname); | ||
34 | size_t fsize = st.st_size; | ||
35 | mapv = mmap(NULL, fsize, PROT_READ, MAP_PRIVATE, fd, 0); | ||
36 | if (mapv == MAP_FAILED) | ||
37 | err(1, "%s: mmap", fname); | ||
38 | unsigned char *map = (unsigned char *)mapv; | ||
39 | |||
40 | if (le(map, fsize, 0, 2) != (('Z'<<8) | 'M')) { | ||
41 | warnx("MsiGetFileInfo(%s) -> no MZ", fname); | ||
42 | goto cleanup; | ||
22 | } | 43 | } |
23 | if (language) { | 44 | unsigned pe_pos = le(map, fsize, 0x3c, 4); |
24 | c16cpy(language, language_size, "2057"); | 45 | if (le(map, fsize, pe_pos, 4) != (('E'<<8) | 'P')) { |
46 | warnx("MsiGetFileInfo(%s) -> no PE", fname); | ||
47 | goto cleanup; | ||
25 | } | 48 | } |
26 | return 0; | 49 | pe_pos += 4; /* skip to the main header */ |
50 | unsigned nsections = le(map, fsize, pe_pos + 2, 2); | ||
51 | unsigned opthdr_size = le(map, fsize, pe_pos + 16, 2); | ||
52 | unsigned opthdr_pos = pe_pos + 20; | ||
53 | /* bool sixtyfourbit = le(map, fsize, opthdr_pos, 2) == 0x020B; */ | ||
54 | unsigned secthdr_pos = opthdr_pos + opthdr_size; | ||
55 | while (nsections > 0) { | ||
56 | if (le(map, fsize, secthdr_pos+0, 1) == '.' && | ||
57 | le(map, fsize, secthdr_pos+1, 1) == 'r' && | ||
58 | le(map, fsize, secthdr_pos+2, 1) == 's' && | ||
59 | le(map, fsize, secthdr_pos+3, 1) == 'r' && | ||
60 | le(map, fsize, secthdr_pos+4, 1) == 'c' && | ||
61 | le(map, fsize, secthdr_pos+5, 1) == 0) | ||
62 | goto found_resource_section; | ||
63 | secthdr_pos += 0x28; | ||
64 | nsections--; | ||
65 | } | ||
66 | warnx("MsiGetFileInfo(%s) -> no .rsrc", fname); | ||
67 | goto cleanup; | ||
68 | |||
69 | found_resource_section:; | ||
70 | unsigned rsrc_size = le(map, fsize, secthdr_pos+8, 4); | ||
71 | unsigned rsrc_offset = le(map, fsize, secthdr_pos+20, 4); | ||
72 | unsigned rsrc_vaddr = le(map, fsize, secthdr_pos+12, 4); | ||
73 | |||
74 | unsigned res_dir_offset = rsrc_offset; | ||
75 | unsigned nnamed, nid; | ||
76 | nnamed = le(map, fsize, res_dir_offset+12, 2); | ||
77 | nid = le(map, fsize, res_dir_offset+14, 2); | ||
78 | for (unsigned i = nnamed; i < nnamed+nid; i++) { | ||
79 | unsigned id = le(map, fsize, res_dir_offset + 16 + 8*i, 4); | ||
80 | unsigned entry = le(map, fsize, res_dir_offset + 16 + 8*i + 4, 4); | ||
81 | if (id == 16 && (entry & 0x80000000)) { | ||
82 | res_dir_offset = rsrc_offset + (entry & 0x7FFFFFFF); | ||
83 | goto found_versioninfo_toplevel; | ||
84 | } | ||
85 | } | ||
86 | warnx("MsiGetFileInfo(%s) -> no top-level numeric key 16 for versioninfo", fname); | ||
87 | goto cleanup; | ||
88 | |||
89 | found_versioninfo_toplevel: | ||
90 | nnamed = le(map, fsize, res_dir_offset+12, 2); | ||
91 | nid = le(map, fsize, res_dir_offset+14, 2); | ||
92 | for (unsigned i = 0; i < nnamed+nid; i++) { | ||
93 | unsigned entry = le(map, fsize, res_dir_offset + 16 + 8*i + 4, 4); | ||
94 | if (entry & 0x80000000) { | ||
95 | res_dir_offset = rsrc_offset + (entry & 0x7FFFFFFF); | ||
96 | goto found_versioninfo_2ndlevel; | ||
97 | } | ||
98 | } | ||
99 | warnx("MsiGetFileInfo(%s) -> no 2nd-level subdir", fname); | ||
100 | goto cleanup; | ||
101 | |||
102 | found_versioninfo_2ndlevel: | ||
103 | nnamed = le(map, fsize, res_dir_offset+12, 2); | ||
104 | nid = le(map, fsize, res_dir_offset+14, 2); | ||
105 | for (unsigned i = 0; i < nnamed+nid; i++) { | ||
106 | unsigned entry = le(map, fsize, res_dir_offset + 16 + 8*i + 4, 4); | ||
107 | if (!(entry & 0x80000000)) { | ||
108 | res_dir_offset = rsrc_offset + (entry & 0x7FFFFFFF); | ||
109 | goto found_versioninfo_3rdlevel; | ||
110 | } | ||
111 | } | ||
112 | warnx("MsiGetFileInfo(%s) -> no 3rd-level resource data", fname); | ||
113 | goto cleanup; | ||
114 | |||
115 | found_versioninfo_3rdlevel:; | ||
116 | unsigned versioninfo_offset = le(map, fsize, res_dir_offset, 4); | ||
117 | unsigned versioninfo_size = le(map, fsize, res_dir_offset+4, 4); | ||
118 | versioninfo_offset = rsrc_offset + versioninfo_offset - rsrc_vaddr; | ||
119 | |||
120 | unsigned name_offset = versioninfo_offset + 6; | ||
121 | const char *next_name_chr = "VS_VERSION_INFO"; | ||
122 | do { | ||
123 | if (le(map, fsize, name_offset, 2) != *next_name_chr) | ||
124 | goto cleanup; /* identifying string didn't match */ | ||
125 | name_offset += 2; | ||
126 | } while (*next_name_chr++); | ||
127 | unsigned fixed_offset = (name_offset + 3) & ~3; | ||
128 | if (le(map, fsize, fixed_offset, 4) != 0xFEEF04BDU) { | ||
129 | warnx("MsiGetFileInfo(%s) -> no VS_FIXEDFILEINFO magic number", fname); | ||
130 | goto cleanup; | ||
131 | } | ||
132 | int four_part_version[4]; | ||
133 | four_part_version[0] = le(map, fsize, fixed_offset + 10, 2); | ||
134 | four_part_version[1] = le(map, fsize, fixed_offset + 8, 2); | ||
135 | four_part_version[2] = le(map, fsize, fixed_offset + 14, 2); | ||
136 | four_part_version[3] = le(map, fsize, fixed_offset + 12, 2); | ||
137 | unsigned child_offset = fixed_offset + | ||
138 | le(map, fsize, versioninfo_offset+2, 2); | ||
139 | unsigned lcid; | ||
140 | while (child_offset < versioninfo_offset + versioninfo_size) { | ||
141 | unsigned this_child_offset = child_offset; | ||
142 | child_offset += le(map, fsize, child_offset, 2); | ||
143 | child_offset = (child_offset + 3) &~ 3; | ||
144 | if (child_offset <= this_child_offset) { | ||
145 | warnx("MsiGetFileInfo(%s) -> bad length field", fname); | ||
146 | goto cleanup; | ||
147 | } | ||
148 | const char *next_name_chr = "VarFileInfo"; | ||
149 | name_offset = this_child_offset + 6; | ||
150 | do { | ||
151 | if (le(map, fsize, name_offset, 2) != *next_name_chr) | ||
152 | goto this_is_not_a_varfileinfo; | ||
153 | name_offset += 2; | ||
154 | } while (*next_name_chr++); | ||
155 | unsigned subchild_offset = (name_offset + 3) & ~3; | ||
156 | |||
157 | next_name_chr = "Translation"; | ||
158 | name_offset = subchild_offset + 6; | ||
159 | do { | ||
160 | if (le(map, fsize, name_offset, 2) != *next_name_chr) { | ||
161 | warnx("MsiGetFileInfo(%s) -> child not called 'Translation'", fname); | ||
162 | goto cleanup; | ||
163 | } | ||
164 | name_offset += 2; | ||
165 | } while (*next_name_chr++); | ||
166 | subchild_offset = (name_offset + 3) & ~3; | ||
167 | lcid = le(map, fsize, subchild_offset, 2); | ||
168 | goto success; | ||
169 | this_is_not_a_varfileinfo: | ||
170 | continue; | ||
171 | } | ||
172 | warnx("MsiGetFileInfo(%s) -> no VarFileInfo found", fname); | ||
173 | goto cleanup; | ||
174 | |||
175 | success:; | ||
176 | char verbuf[256], langbuf[256]; | ||
177 | snprintf(verbuf, sizeof(verbuf), "%d.%d.%d.%d", | ||
178 | four_part_version[0], four_part_version[1], | ||
179 | four_part_version[2], four_part_version[3]); | ||
180 | snprintf(langbuf, sizeof(langbuf), "%u", lcid); | ||
181 | warnx("MsiGetFileInfo(%s) -> version %s lang %s", fname, verbuf, langbuf); | ||
182 | if (version) | ||
183 | c16cpy(version, version_size, verbuf); | ||
184 | if (language) | ||
185 | c16cpy(language, language_size, langbuf); | ||
186 | toret = 0; | ||
187 | |||
188 | cleanup: | ||
189 | if (mapv != MAP_FAILED) | ||
190 | munmap(mapv, fsize); | ||
191 | if (fd != -1) | ||
192 | close(fd); | ||
193 | sfree(fname); | ||
194 | return toret; | ||
195 | } | ||
27 | } | 196 | } |
28 | 197 | ||
29 | typedef struct MsiTypePrefix { | 198 | typedef struct MsiTypePrefix { |