Line data Source code
1 : /* src/vm/suck.cpp - functions to read LE ordered types from a buffer
2 :
3 : Copyright (C) 1996-2012
4 : CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
5 :
6 : This file is part of CACAO.
7 :
8 : This program is free software; you can redistribute it and/or
9 : modify it under the terms of the GNU General Public License as
10 : published by the Free Software Foundation; either version 2, or (at
11 : your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful, but
14 : WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 : General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program; if not, write to the Free Software
20 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 : 02110-1301, USA.
22 :
23 : */
24 :
25 :
26 : #include "config.h"
27 :
28 : #include <cassert>
29 : #include <cstdlib>
30 :
31 : #include "vm/types.hpp"
32 :
33 : #include "mm/memory.hpp"
34 :
35 : #include "threads/mutex.hpp"
36 :
37 : #include "toolbox/buffer.hpp"
38 : #include "toolbox/endianess.hpp"
39 : #include "toolbox/hashtable.hpp"
40 : #include "toolbox/list.hpp"
41 : #include "toolbox/logging.hpp"
42 :
43 : #include "vm/exceptions.hpp"
44 : #include "vm/loader.hpp"
45 : #include "vm/options.hpp"
46 : #include "vm/os.hpp"
47 : #include "vm/properties.hpp"
48 : #include "vm/suck.hpp"
49 : #include "vm/vm.hpp"
50 : #include "vm/zip.hpp"
51 :
52 : using namespace cacao;
53 :
54 :
55 : /* scandir_filter **************************************************************
56 :
57 : Filters for zip/jar files.
58 :
59 : *******************************************************************************/
60 :
61 0 : static int scandir_filter(const struct dirent *a)
62 : {
63 : s4 namlen;
64 :
65 : #if defined(_DIRENT_HAVE_D_NAMLEN)
66 : namlen = a->d_namlen;
67 : #else
68 0 : namlen = strlen(a->d_name);
69 : #endif
70 :
71 0 : if ((strncasecmp(a->d_name + namlen - 4, ".zip", 4) == 0) ||
72 : (strncasecmp(a->d_name + namlen - 4, ".jar", 4) == 0))
73 0 : return 1;
74 :
75 0 : return 0;
76 : }
77 :
78 :
79 : /**
80 : * Adds a classpath to the global classpath entries list.
81 : */
82 163 : void SuckClasspath::add(char *classpath)
83 : {
84 : char *start;
85 : char *end;
86 : char *cwd;
87 : s4 cwdlen;
88 :
89 : /* parse the classpath string */
90 :
91 653 : for (start = classpath; (*start) != '\0'; ) {
92 :
93 : /* search for ':' delimiter to get the end of the current entry */
94 327 : for (end = start; ((*end) != '\0') && ((*end) != ':'); end++);
95 :
96 327 : if (start != end) {
97 327 : bool is_zip = false;
98 327 : size_t filenamelen = end - start;
99 :
100 327 : if (filenamelen > 4) {
101 327 : if ((strncasecmp(end - 4, ".zip", 4) == 0) ||
102 : (strncasecmp(end - 4, ".jar", 4) == 0)) {
103 164 : is_zip = true;
104 : }
105 : }
106 :
107 : /* save classpath entries as absolute pathnames */
108 :
109 327 : cwd = NULL;
110 327 : cwdlen = 0;
111 :
112 327 : if (*start != '/') { /* XXX fix me for win32 */
113 164 : cwd = os::getcwd();
114 164 : cwdlen = strlen(cwd) + strlen("/");
115 : }
116 :
117 : /* allocate memory for filename and fill it */
118 :
119 327 : char *filename = MNEW(char, filenamelen + cwdlen + strlen("/") + strlen("0"));
120 :
121 327 : if (cwd) {
122 164 : strcpy(filename, cwd);
123 164 : strcat(filename, "/");
124 164 : strncat(filename, start, filenamelen);
125 :
126 : /* add cwd length to file length */
127 164 : filenamelen += cwdlen;
128 :
129 : } else {
130 163 : strncpy(filename, start, filenamelen);
131 163 : filename[filenamelen] = '\0';
132 : }
133 :
134 327 : if (is_zip) {
135 : #if defined(ENABLE_ZLIB)
136 164 : if (ZipFile *zip = ZipFile::open(filename)) {
137 164 : list_classpath_entry *lce = NEW(list_classpath_entry);
138 :
139 164 : lce->type = CLASSPATH_ARCHIVE;
140 164 : lce->zip = zip;
141 164 : lce->path = filename;
142 164 : lce->pathlen = filenamelen;
143 :
144 : /* SUN compatible -verbose:class output */
145 :
146 164 : if (opt_verboseclass)
147 0 : printf("[Opened %s]\n", filename);
148 :
149 164 : push_back(lce);
150 : }
151 : #else
152 : os::abort("suck_add: zip/jar files not supported");
153 : #endif
154 : }
155 : else {
156 163 : if (filename[filenamelen - 1] != '/') {/* XXX fixme for win32 */
157 163 : filename[filenamelen] = '/';
158 163 : filename[filenamelen + 1] = '\0';
159 163 : filenamelen++;
160 : }
161 :
162 163 : list_classpath_entry *lce = NEW(list_classpath_entry);
163 :
164 163 : lce->type = CLASSPATH_PATH;
165 163 : lce->path = filename;
166 163 : lce->pathlen = filenamelen;
167 :
168 163 : push_back(lce);
169 : }
170 : }
171 :
172 : /* goto next classpath entry, skip ':' delimiter */
173 :
174 327 : if ((*end) == ':')
175 164 : start = end + 1;
176 : else
177 163 : start = end;
178 : }
179 163 : }
180 :
181 :
182 : /**
183 : * Adds a classpath form a property entry to the global classpath
184 : * entries list.
185 : */
186 163 : void SuckClasspath::add_from_property(const char *key)
187 : {
188 : const char *value;
189 : const char *start;
190 : const char *end;
191 : s4 pathlen;
192 : struct dirent **namelist;
193 : s4 n;
194 : s4 i;
195 : s4 namlen;
196 : char *p;
197 163 : char* boot_class_path = NULL;
198 :
199 : // Get the property value.
200 163 : Properties& properties = VM::get_current()->get_properties();
201 163 : value = properties.get(key);
202 :
203 163 : if (value == NULL)
204 0 : return;
205 :
206 : /* get the directory entries of the property */
207 :
208 489 : for (start = value; (*start) != '\0'; ) {
209 :
210 : /* search for ':' delimiter to get the end of the current entry */
211 :
212 163 : for (end = start; ((*end) != '\0') && ((*end) != ':'); end++);
213 :
214 : /* found an entry */
215 :
216 163 : if (start != end) {
217 : /* allocate memory for the path entry */
218 :
219 163 : pathlen = end - start;
220 163 : char* path = MNEW(char, pathlen + strlen("0"));
221 :
222 : /* copy and terminate the string */
223 :
224 163 : strncpy(path, start, pathlen);
225 163 : path[pathlen] = '\0';
226 :
227 : /* Reset namelist to NULL for the freeing in an error case
228 : (see below). */
229 :
230 163 : namelist = NULL;
231 :
232 : /* scan the directory found for zip/jar files */
233 :
234 163 : n = os::scandir((const char*) path, &namelist, &scandir_filter, (int (*)(const void*, const void*)) &alphasort);
235 :
236 : /* On error, just continue, this should be ok. */
237 :
238 163 : if (n > 0) {
239 0 : for (i = 0; i < n; i++) {
240 : #if defined(_DIRENT_HAVE_D_NAMLEN)
241 : namlen = namelist[i]->d_namlen;
242 : #else
243 0 : namlen = strlen(namelist[i]->d_name);
244 : #endif
245 :
246 0 : if (boot_class_path == NULL) {
247 : /* Allocate memory for bootclasspath. */
248 : p = MNEW(char,
249 : pathlen + strlen("/") + namlen +
250 0 : strlen("0"));
251 :
252 0 : strcpy(p, path);
253 0 : strcat(p, "/");
254 0 : strcat(p, namelist[i]->d_name);
255 :
256 : } else {
257 : /* Allocate memory for bootclasspath. */
258 : p = MNEW(char,
259 : pathlen + strlen("/") + namlen +
260 : strlen(":") +
261 : strlen(boot_class_path) +
262 0 : strlen("0"));
263 :
264 : /* Append the file found to the bootclasspath. */
265 :
266 0 : strcpy(p, boot_class_path);
267 0 : strcat(p, ":");
268 0 : strcat(p, path);
269 0 : strcat(p, "/");
270 0 : strcat(p, namelist[i]->d_name);
271 :
272 0 : MFREE(boot_class_path, char, strlen(boot_class_path));
273 : }
274 :
275 0 : boot_class_path = p;
276 :
277 : /* free the memory allocated by scandir */
278 : /* (We use `free` as the memory came from the C library.) */
279 :
280 0 : free(namelist[i]);
281 : }
282 : }
283 :
284 : /* On some systems (like Linux) when n == 0, then namelist
285 : returned from scnadir is NULL, thus we don't have to
286 : free it.
287 : (Use `free` as the memory came from the C library.) */
288 :
289 163 : if (namelist != NULL)
290 0 : free(namelist);
291 :
292 163 : MFREE(path, char, pathlen + strlen("0"));
293 : }
294 :
295 : /* goto next entry, skip ':' delimiter */
296 :
297 163 : if ((*end) == ':')
298 0 : start = end + 1;
299 : else
300 163 : start = end;
301 : }
302 :
303 163 : if (boot_class_path != NULL) {
304 : // only update if something has changed
305 :
306 : // FIXME Make boot_class_path const char*.
307 0 : char* old_boot_class_path = (char*) properties.get("sun.boot.class.path");
308 :
309 : p = MNEW(char,
310 : strlen(boot_class_path) +
311 : strlen(":") +
312 : strlen(old_boot_class_path) +
313 0 : strlen("0"));
314 :
315 : /* Prepend the file found to the bootclasspath. */
316 :
317 0 : strcpy(p, boot_class_path);
318 0 : strcat(p, ":");
319 0 : strcat(p, old_boot_class_path);
320 :
321 0 : MFREE(boot_class_path, char, strlen(boot_class_path));
322 0 : MFREE(old_boot_class_path, char, strlen(old_boot_class_path));
323 :
324 : /* Prepend the file found to the bootclasspath. */
325 0 : properties.put("sun.boot.class.path", p);
326 0 : properties.put("java.boot.class.path", p);
327 : }
328 :
329 : }
330 :
331 :
332 71705 : inline void ClassBuffer::init(classinfo *clazz, uint8_t *data, size_t sz, const char *path) {
333 71705 : this->clazz = clazz;
334 71705 : this->data = data;
335 71705 : this->pos = data;
336 71705 : this->end = data + sz;
337 71705 : this->path = path;
338 71705 : }
339 :
340 644 : ClassBuffer::ClassBuffer(classinfo *clazz, uint8_t *data, size_t sz, const char *path) {
341 644 : init(clazz, data, sz, path);
342 644 : }
343 :
344 0 : ClassFileVersion ClassBuffer::version() const { return clazz->version; }
345 :
346 : /***
347 : * Loads class file corresponding to given classinfo into new ClassBuffer.
348 : * All directories of the searchpath are used to find the classfile (<classname>.class).
349 : * Use operator bool to check if initialization was successfull.
350 : */
351 35915 : ClassBuffer::ClassBuffer(classinfo *c) {
352 35915 : init(NULL, NULL, 0, NULL);
353 :
354 : // get the classname as char string
355 : // (do it here for the warning at the end of the function)
356 :
357 35915 : size_t filenamelen = c->name.size() + strlen(".class") + strlen("0");
358 :
359 35915 : Buffer<> filename(filenamelen);
360 35915 : Buffer<> path;
361 :
362 : filename.write(c->name)
363 35915 : .write(".class");
364 :
365 : // Get current list of classpath entries.
366 35915 : SuckClasspath& suckclasspath = VM::get_current()->get_suckclasspath();
367 :
368 : // walk through all classpath entries
369 :
370 70178 : for (SuckClasspath::iterator it = suckclasspath.begin(); it != suckclasspath.end(); it++) {
371 69409 : list_classpath_entry *lce = *it;
372 :
373 : #if defined(ENABLE_ZLIB)
374 69409 : if (lce->type == CLASSPATH_ARCHIVE) {
375 :
376 : // enter a monitor on zip/jar archives
377 33494 : MutexLocker lock(*lce->mutex);
378 :
379 : // try to get the file in current archive
380 33494 : if (ZipFile::EntryRef zip = lce->zip->find(c->name)) {
381 : // found class, fill in classbuffer
382 32723 : size_t size = zip->uncompressedsize;
383 32723 : uint8_t *data = MNEW(uint8_t, size);
384 :
385 32723 : zip->get(data);
386 :
387 32723 : init(c, data, size, lce->path);
388 : return;
389 0 : }
390 : } else {
391 : #endif /* defined(ENABLE_ZLIB) */
392 35915 : path.reset();
393 :
394 : path.write(lce->path)
395 35915 : .write(filename);
396 :
397 35915 : if (FILE *classfile = os::fopen(path.c_str(), "r")) {
398 : struct stat stat_buffer;
399 :
400 2423 : if (os::stat(path.c_str(), &stat_buffer) == -1)
401 0 : continue;
402 :
403 2423 : size_t size = stat_buffer.st_size;
404 2423 : uint8_t *data = MNEW(u1, size);
405 :
406 : // read class data
407 2423 : size_t bytes_read = os::fread(data, 1, size, classfile);
408 2423 : os::fclose(classfile);
409 :
410 2423 : if (bytes_read != size) {
411 0 : free();
412 : return;
413 : }
414 :
415 2423 : init(c, data, size, lce->path);
416 : return;
417 : }
418 : #if defined(ENABLE_ZLIB)
419 : }
420 : #endif
421 : }
422 :
423 : // if we get here, we could not find the file
424 769 : if (opt_verbose)
425 0 : dolog("Warning: Can not open class file '%s'", filename.c_str());
426 : }
427 :
428 :
429 : /* suck_stop *******************************************************************
430 :
431 : Frees memory for buffer with classfile data.
432 :
433 : CAUTION: This function may only be called if buffer has been
434 : allocated by suck_start with reading a file.
435 :
436 : *******************************************************************************/
437 :
438 35146 : void ClassBuffer::free() {
439 : // free memory
440 :
441 35146 : MFREE(data, u1, end - data);
442 35146 : }
443 :
444 :
445 : /*
446 : * These are local overrides for various environment variables in Emacs.
447 : * Please do not remove this and leave it at the end of the file, where
448 : * Emacs will automagically detect them.
449 : * ---------------------------------------------------------------------
450 : * Local variables:
451 : * mode: c++
452 : * indent-tabs-mode: t
453 : * c-basic-offset: 4
454 : * tab-width: 4
455 : * End:
456 : * vim:noexpandtab:sw=4:ts=4:
457 : */
|