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 */
30static void
31add_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
49struct 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
63enum vtype {
64	ROCKRIDGE,
65	JOLIET,
66	ISO9660
67};
68
69/*
70 * Verify file
71 */
72static void
73verify_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
140static void
141verify(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
200static int
201create_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
305DEFINE_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