1 /*-
2  * Copyright (c) 2009,2010 Michihiro NAKAJIMA
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 #include "test.h"
26 
27 /*
28  * Check that an ISO 9660 image is correctly created.
29  */
30 static void
add_entry(struct archive * a,const char * fname,const char * sym)31 add_entry(struct archive *a, const char *fname, const char *sym)
32 {
33 	struct archive_entry *ae;
34 
35 	assert((ae = archive_entry_new()) != NULL);
36 	archive_entry_set_birthtime(ae, 2, 20);
37 	archive_entry_set_atime(ae, 3, 30);
38 	archive_entry_set_ctime(ae, 4, 40);
39 	archive_entry_set_mtime(ae, 5, 50);
40 	archive_entry_copy_pathname(ae, fname);
41 	if (sym != NULL)
42 		archive_entry_set_symlink(ae, sym);
43 	archive_entry_set_mode(ae, S_IFREG | 0555);
44 	archive_entry_set_size(ae, 0);
45 	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
46 	archive_entry_free(ae);
47 }
48 
49 struct fns {
50 	size_t	maxlen;
51 	size_t	longest_len;
52 	size_t	maxflen;
53 	size_t	maxelen;
54 	size_t	alloc;
55 	int	cnt;
56 	char	**names;
57 	int	opt;
58 #define	UPPER_CASE_ONLY	0x00001
59 #define	ONE_DOT		0x00002
60 #define	ALLOW_LDOT	0x00004
61 };
62 
63 enum vtype {
64 	ROCKRIDGE,
65 	JOLIET,
66 	ISO9660
67 };
68 
69 /*
70  * Verify file
71  */
72 static void
verify_file(struct archive * a,enum vtype type,struct fns * fns)73 verify_file(struct archive *a, enum vtype type, struct fns *fns)
74 {
75 	struct archive_entry *ae;
76 	int i;
77 
78 	assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
79 	if (type == ROCKRIDGE) {
80 		assertEqualInt(2, archive_entry_birthtime(ae));
81 		assertEqualInt(3, archive_entry_atime(ae));
82 		assertEqualInt(4, archive_entry_ctime(ae));
83 	} else {
84 		assertEqualInt(0, archive_entry_birthtime_is_set(ae));
85 		assertEqualInt(5, archive_entry_atime(ae));
86 		assertEqualInt(5, archive_entry_ctime(ae));
87 	}
88 	assertEqualInt(5, archive_entry_mtime(ae));
89 	if (type == ROCKRIDGE)
90 		assert((S_IFREG | 0555) == archive_entry_mode(ae));
91 	else
92 		assert((S_IFREG | 0400) == archive_entry_mode(ae));
93 	assertEqualInt(0, archive_entry_size(ae));
94 
95 	/*
96 	 * Check if the same filename does not appear.
97 	 */
98 	for (i = 0; i < fns->cnt; i++) {
99 		const char *p;
100 		const char *pathname = archive_entry_pathname(ae);
101 		const char *symlinkname = archive_entry_symlink(ae);
102 		size_t length;
103 
104 		if (symlinkname != NULL) {
105 			length = strlen(symlinkname);
106 			assert(length == 1 || length == 128 || length == 255);
107 			assertEqualInt(symlinkname[length-1], 'x');
108 		}
109 		failure("Found duplicate for %s", pathname);
110 		assert(strcmp(fns->names[i], pathname) != 0);
111 		assert((length = strlen(pathname)) <= fns->maxlen);
112 		if (length > fns->longest_len)
113 			fns->longest_len = length;
114 		p = strrchr(pathname, '.');
115 		if (p != NULL) {
116 			/* Check a length of file name. */
117 			assert((size_t)(p - pathname) <= fns->maxflen);
118 			/* Check a length of file extension. */
119 			assert(strlen(p+1) <= fns->maxelen);
120 			if (fns->opt & ONE_DOT) {
121 				/* Do not have multi dot. */
122 				assert(strchr(pathname, '.') == p);
123 			}
124 		}
125 		for (p = pathname; *p; p++) {
126 			if (fns->opt & UPPER_CASE_ONLY) {
127 				/* Do not have any lower-case character. */
128 				assert(*p < 'a' || *p > 'z');
129 			} else
130 				break;
131 		}
132 		if ((fns->opt & ALLOW_LDOT) == 0)
133 			/* Do not have a dot at the first position. */
134 			assert(*pathname != '.');
135 	}
136 	/* Save the filename which is appeared to use above next time. */
137 	fns->names[fns->cnt++] = strdup(archive_entry_pathname(ae));
138 }
139 
140 static void
verify(unsigned char * buff,size_t used,enum vtype type,struct fns * fns)141 verify(unsigned char *buff, size_t used, enum vtype type, struct fns *fns)
142 {
143 	struct archive *a;
144 	struct archive_entry *ae;
145 	size_t i;
146 
147 	/*
148 	 * Read ISO image.
149 	 */
150 	assert((a = archive_read_new()) != NULL);
151 	assertEqualIntA(a, 0, archive_read_support_format_all(a));
152 	assertEqualIntA(a, 0, archive_read_support_filter_all(a));
153 	if (type >= 1)
154 		assertA(0 == archive_read_set_option(a, NULL, "rockridge",
155 		    NULL));
156 	if (type >= 2)
157 		assertA(0 == archive_read_set_option(a, NULL, "joliet",
158 		    NULL));
159 	assertEqualIntA(a, 0, archive_read_open_memory(a, buff, used));
160 
161 	/*
162 	 * Read Root Directory
163 	 * Root Directory entry must be in ISO image.
164 	 */
165 	assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
166 	assertEqualInt(archive_entry_atime(ae), archive_entry_ctime(ae));
167 	assertEqualInt(archive_entry_atime(ae), archive_entry_mtime(ae));
168 	assertEqualString(".", archive_entry_pathname(ae));
169 	switch (type) {
170 	case ROCKRIDGE:
171 		assert((S_IFDIR | 0555) == archive_entry_mode(ae));
172 		break;
173 	case JOLIET:
174 		assert((S_IFDIR | 0700) == archive_entry_mode(ae));
175 		break;
176 	case ISO9660:
177 		assert((S_IFDIR | 0700) == archive_entry_mode(ae));
178 		break;
179 	}
180 
181 	/*
182 	 * Verify file status.
183 	 */
184 	memset(fns->names, 0, sizeof(char *) * fns->alloc);
185 	fns->cnt = 0;
186 	for (i = 0; i < fns->alloc; i++)
187 		verify_file(a, type, fns);
188 	for (i = 0; i < fns->alloc; i++)
189 		free(fns->names[i]);
190 	assertEqualInt((int)fns->longest_len, (int)fns->maxlen);
191 
192 	/*
193 	 * Verify the end of the archive.
194 	 */
195 	assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
196 	assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
197 	assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a));
198 }
199 
200 static int
create_iso_image(unsigned char * buff,size_t buffsize,size_t * used,const char * opt)201 create_iso_image(unsigned char *buff, size_t buffsize, size_t *used,
202     const char *opt)
203 {
204 	struct archive *a;
205 	int i, l, fcnt;
206 	const int lens[] = {
207 	    0, 1, 3, 5, 7, 8, 9, 29, 30, 31, 32,
208 		62, 63, 64, 65, 101, 102, 103, 104,
209 	    191, 192, 193, 194, 204, 205, 206, 207, 208,
210 		252, 253, 254, 255,
211 	    -1 };
212 	char fname1[256];
213 	char fname2[256];
214 	char sym1[2];
215 	char sym128[129];
216 	char sym255[256];
217 
218 	/* ISO9660 format: Create a new archive in memory. */
219 	assert((a = archive_write_new()) != NULL);
220 	assertA(0 == archive_write_set_format_iso9660(a));
221 	assertA(0 == archive_write_add_filter_none(a));
222 	assertA(0 == archive_write_set_option(a, NULL, "pad", NULL));
223 	if (opt)
224 		assertA(0 == archive_write_set_options(a, opt));
225 	assertA(0 == archive_write_set_bytes_per_block(a, 1));
226 	assertA(0 == archive_write_set_bytes_in_last_block(a, 1));
227 	assertA(0 == archive_write_open_memory(a, buff, buffsize, used));
228 
229 	sym1[0] = 'x';
230 	sym1[1] = '\0';
231 	for (i = 0; i < (int)sizeof(sym128)-2; i++)
232 		sym128[i] = 'a';
233 	sym128[sizeof(sym128)-2] = 'x';
234 	sym128[sizeof(sym128)-1] = '\0';
235 	for (i = 0; i < (int)sizeof(sym255)-2; i++)
236 		sym255[i] = 'a';
237 	sym255[sizeof(sym255)-2] = 'x';
238 	sym255[sizeof(sym255)-1] = '\0';
239 
240 	fcnt = 0;
241 	for (i = 0; lens[i] >= 0; i++) {
242 		for (l = 0; l < lens[i]; l++) {
243 			fname1[l] = 'a';
244 			fname2[l] = 'A';
245 		}
246 		if (l > 0) {
247 			fname1[l] = '\0';
248 			fname2[l] = '\0';
249 			add_entry(a, fname1, NULL);
250 			add_entry(a, fname2, sym1);
251 			fcnt += 2;
252 		}
253 		if (l < 254) {
254 			fname1[l] = '.';
255 			fname1[l+1] = 'c';
256 			fname1[l+2] = '\0';
257 			fname2[l] = '.';
258 			fname2[l+1] = 'C';
259 			fname2[l+2] = '\0';
260 			add_entry(a, fname1, sym128);
261 			add_entry(a, fname2, sym255);
262 			fcnt += 2;
263 		}
264 		if (l < 252) {
265 			fname1[l] = '.';
266 			fname1[l+1] = 'p';
267 			fname1[l+2] = 'n';
268 			fname1[l+3] = 'g';
269 			fname1[l+4] = '\0';
270 			fname2[l] = '.';
271 			fname2[l+1] = 'P';
272 			fname2[l+2] = 'N';
273 			fname2[l+3] = 'G';
274 			fname2[l+4] = '\0';
275 			add_entry(a, fname1, NULL);
276 			add_entry(a, fname2, sym1);
277 			fcnt += 2;
278 		}
279 		if (l < 251) {
280 			fname1[l] = '.';
281 			fname1[l+1] = 'j';
282 			fname1[l+2] = 'p';
283 			fname1[l+3] = 'e';
284 			fname1[l+4] = 'g';
285 			fname1[l+5] = '\0';
286 			fname2[l] = '.';
287 			fname2[l+1] = 'J';
288 			fname2[l+2] = 'P';
289 			fname2[l+3] = 'E';
290 			fname2[l+4] = 'G';
291 			fname2[l+5] = '\0';
292 			add_entry(a, fname1, sym128);
293 			add_entry(a, fname2, sym255);
294 			fcnt += 2;
295 		}
296 	}
297 
298 	/* Close out the archive. */
299 	assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
300 	assertEqualIntA(a, ARCHIVE_OK, archive_write_free(a));
301 
302 	return (fcnt);
303 }
304 
DEFINE_TEST(test_write_format_iso9660_filename)305 DEFINE_TEST(test_write_format_iso9660_filename)
306 {
307 	unsigned char *buff;
308 	size_t buffsize = 120 * 2048;
309 	size_t used;
310 	int fcnt;
311 	struct fns fns;
312 
313 	buff = malloc(buffsize);
314 	assert(buff != NULL);
315 	if (buff == NULL)
316 		return;
317 	memset(&fns, 0, sizeof(fns));
318 
319 	/*
320 	 * Create ISO image with no option.
321 	 */
322 	fcnt = create_iso_image(buff, buffsize, &used, NULL);
323 
324 	fns.names = (char **)malloc(sizeof(char *) * fcnt);
325 	assert(fns.names != NULL);
326 	if (fns.names == NULL) {
327 		free(buff);
328 		return;
329 	}
330 	fns.alloc = fcnt;
331 
332 	/* Verify rockridge filenames. */
333 	fns.longest_len = 0;
334 	fns.maxlen = fns.maxflen = fns.maxelen = 255;
335 	fns.opt = ALLOW_LDOT;
336 	verify(buff, used, ROCKRIDGE, &fns);
337 
338 	/* Verify joliet filenames. */
339 	fns.longest_len = 0;
340 	fns.maxlen = fns.maxflen = fns.maxelen = 64;
341 	fns.opt = ALLOW_LDOT;
342 	verify(buff, used, JOLIET, &fns);
343 
344 	/* Verify ISO9660 filenames. */
345 	fns.longest_len = 0;
346 	fns.maxlen = 8+3+1;
347 	fns.maxflen = 8;
348 	fns.maxelen = 3;
349 	fns.opt = UPPER_CASE_ONLY | ONE_DOT;
350 	verify(buff, used, ISO9660, &fns);
351 
352 	/*
353 	 * Create ISO image with iso-level=2.
354 	 */
355 	assertEqualInt(fcnt, create_iso_image(buff, buffsize, &used,
356 	    "iso-level=2"));
357 
358 	/* Verify rockridge filenames. */
359 	fns.longest_len = 0;
360 	fns.maxlen = fns.maxflen = fns.maxelen = 255;
361 	fns.opt = ALLOW_LDOT;
362 	verify(buff, used, ROCKRIDGE, &fns);
363 
364 	/* Verify joliet filenames. */
365 	fns.longest_len = 0;
366 	fns.maxlen = fns.maxflen = fns.maxelen = 64;
367 	fns.opt = ALLOW_LDOT;
368 	verify(buff, used, JOLIET, &fns);
369 
370 	/* Verify ISO9660 filenames. */
371 	fns.longest_len = 0;
372 	fns.maxlen = 31;
373 	fns.maxflen = 30;
374 	fns.maxelen = 30;
375 	fns.opt = UPPER_CASE_ONLY | ONE_DOT;
376 	verify(buff, used, ISO9660, &fns);
377 
378 	/*
379 	 * Create ISO image with iso-level=3.
380 	 */
381 	assertEqualInt(fcnt, create_iso_image(buff, buffsize, &used,
382 	    "iso-level=3"));
383 
384 	/* Verify rockridge filenames. */
385 	fns.longest_len = 0;
386 	fns.maxlen = fns.maxflen = fns.maxelen = 255;
387 	fns.opt = ALLOW_LDOT;
388 	verify(buff, used, ROCKRIDGE, &fns);
389 
390 	/* Verify joliet filenames. */
391 	fns.longest_len = 0;
392 	fns.maxlen = fns.maxflen = fns.maxelen = 64;
393 	fns.opt = ALLOW_LDOT;
394 	verify(buff, used, JOLIET, &fns);
395 
396 	/* Verify ISO9660 filenames. */
397 	fns.longest_len = 0;
398 	fns.maxlen = 31;
399 	fns.maxflen = 30;
400 	fns.maxelen = 30;
401 	fns.opt = UPPER_CASE_ONLY | ONE_DOT;
402 	verify(buff, used, ISO9660, &fns);
403 
404 	/*
405 	 * Create ISO image with iso-level=4.
406 	 */
407 	assertEqualInt(fcnt, create_iso_image(buff, buffsize, &used,
408 	    "iso-level=4"));
409 
410 	/* Verify rockridge filenames. */
411 	fns.longest_len = 0;
412 	fns.maxlen = fns.maxflen = fns.maxelen = 255;
413 	fns.opt = ALLOW_LDOT;
414 	verify(buff, used, ROCKRIDGE, &fns);
415 
416 	/* Verify joliet filenames. */
417 	fns.longest_len = 0;
418 	fns.maxlen = fns.maxflen = fns.maxelen = 64;
419 	fns.opt = ALLOW_LDOT;
420 	verify(buff, used, JOLIET, &fns);
421 
422 	/* Verify ISO9660 filenames. */
423 	fns.longest_len = 0;
424 	fns.maxlen = fns.maxflen = fns.maxelen = 193;
425 	fns.opt = ALLOW_LDOT;
426 	verify(buff, used, ISO9660, &fns);
427 
428 	/*
429 	 * Create ISO image with iso-level=4 and !rockridge.
430 	 */
431 	assertEqualInt(fcnt, create_iso_image(buff, buffsize, &used,
432 	    "iso-level=4,!rockridge"));
433 
434 	/* Verify joliet filenames. */
435 	fns.longest_len = 0;
436 	fns.maxlen = fns.maxflen = fns.maxelen = 64;
437 	fns.opt = ALLOW_LDOT;
438 	verify(buff, used, JOLIET, &fns);
439 
440 	/* Verify ISO9660 filenames. */
441 	fns.longest_len = 0;
442 	fns.maxlen = fns.maxflen = fns.maxelen = 207;
443 	fns.opt = ALLOW_LDOT;
444 	verify(buff, used, ISO9660, &fns);
445 
446 	/*
447 	 * Create ISO image with joliet=long.
448 	 */
449 	assertEqualInt(fcnt, create_iso_image(buff, buffsize, &used,
450 	    "joliet=long"));
451 
452 	/* Verify rockridge filenames. */
453 	fns.longest_len = 0;
454 	fns.maxlen = fns.maxflen = fns.maxelen = 255;
455 	fns.opt = ALLOW_LDOT;
456 	verify(buff, used, ROCKRIDGE, &fns);
457 
458 	/* Verify joliet filenames. */
459 	fns.longest_len = 0;
460 	fns.maxlen = fns.maxflen = fns.maxelen = 103;
461 	fns.opt = ALLOW_LDOT;
462 	verify(buff, used, JOLIET, &fns);
463 
464 	/* Verify ISO9660 filenames. */
465 	fns.longest_len = 0;
466 	fns.maxlen = 8+3+1;
467 	fns.maxflen = 8;
468 	fns.maxelen = 3;
469 	fns.opt = UPPER_CASE_ONLY | ONE_DOT;
470 	verify(buff, used, ISO9660, &fns);
471 
472 	free(fns.names);
473 	free(buff);
474 }
475