1231200Smm/*-
2231200Smm * Copyright (c) 2009,2010 Michihiro NAKAJIMA
3231200Smm * All rights reserved.
4231200Smm *
5231200Smm * Redistribution and use in source and binary forms, with or without
6231200Smm * modification, are permitted provided that the following conditions
7231200Smm * are met:
8231200Smm * 1. Redistributions of source code must retain the above copyright
9231200Smm *    notice, this list of conditions and the following disclaimer.
10231200Smm * 2. Redistributions in binary form must reproduce the above copyright
11231200Smm *    notice, this list of conditions and the following disclaimer in the
12231200Smm *    documentation and/or other materials provided with the distribution.
13231200Smm *
14231200Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15231200Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16231200Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17231200Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18231200Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19231200Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20231200Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21231200Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22231200Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23231200Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24231200Smm */
25231200Smm#include "test.h"
26231200Smm
27231200Smm/*
28231200Smm * Check that an ISO 9660 image is correctly created.
29231200Smm */
30231200Smmstatic void
31231200Smmadd_entry(struct archive *a, const char *fname, const char *sym)
32231200Smm{
33231200Smm	struct archive_entry *ae;
34231200Smm
35231200Smm	assert((ae = archive_entry_new()) != NULL);
36231200Smm	archive_entry_set_birthtime(ae, 2, 20);
37231200Smm	archive_entry_set_atime(ae, 3, 30);
38231200Smm	archive_entry_set_ctime(ae, 4, 40);
39231200Smm	archive_entry_set_mtime(ae, 5, 50);
40231200Smm	archive_entry_copy_pathname(ae, fname);
41231200Smm	if (sym != NULL)
42231200Smm		archive_entry_set_symlink(ae, sym);
43231200Smm	archive_entry_set_mode(ae, S_IFREG | 0555);
44231200Smm	archive_entry_set_size(ae, 0);
45231200Smm	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
46231200Smm	archive_entry_free(ae);
47231200Smm}
48231200Smm
49231200Smmstruct fns {
50231200Smm	size_t	maxlen;
51231200Smm	size_t	longest_len;
52231200Smm	size_t	maxflen;
53231200Smm	size_t	maxelen;
54231200Smm	size_t	alloc;
55231200Smm	int	cnt;
56231200Smm	char	**names;
57231200Smm	int	opt;
58231200Smm#define	UPPER_CASE_ONLY	0x00001
59231200Smm#define	ONE_DOT		0x00002
60231200Smm#define	ALLOW_LDOT	0x00004
61231200Smm};
62231200Smm
63231200Smmenum vtype {
64231200Smm	ROCKRIDGE,
65231200Smm	JOLIET,
66231200Smm	ISO9660
67231200Smm};
68231200Smm
69231200Smm/*
70231200Smm * Verify file
71231200Smm */
72231200Smmstatic void
73231200Smmverify_file(struct archive *a, enum vtype type, struct fns *fns)
74231200Smm{
75231200Smm	struct archive_entry *ae;
76231200Smm	int i;
77231200Smm
78231200Smm	assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
79231200Smm	if (type == ROCKRIDGE) {
80231200Smm		assertEqualInt(2, archive_entry_birthtime(ae));
81231200Smm		assertEqualInt(3, archive_entry_atime(ae));
82231200Smm		assertEqualInt(4, archive_entry_ctime(ae));
83231200Smm	} else {
84231200Smm		assertEqualInt(0, archive_entry_birthtime_is_set(ae));
85231200Smm		assertEqualInt(5, archive_entry_atime(ae));
86231200Smm		assertEqualInt(5, archive_entry_ctime(ae));
87231200Smm	}
88231200Smm	assertEqualInt(5, archive_entry_mtime(ae));
89231200Smm	if (type == ROCKRIDGE)
90231200Smm		assert((S_IFREG | 0555) == archive_entry_mode(ae));
91231200Smm	else
92231200Smm		assert((S_IFREG | 0400) == archive_entry_mode(ae));
93231200Smm	assertEqualInt(0, archive_entry_size(ae));
94231200Smm
95231200Smm	/*
96231200Smm	 * Check if the same filename does not appear.
97231200Smm	 */
98231200Smm	for (i = 0; i < fns->cnt; i++) {
99231200Smm		const char *p;
100231200Smm		const char *pathname = archive_entry_pathname(ae);
101232153Smm		const char *symlinkname = archive_entry_symlink(ae);
102231200Smm		size_t length;
103231200Smm
104232153Smm		if (symlinkname != NULL) {
105232153Smm			length = strlen(symlinkname);
106231200Smm			assert(length == 1 || length == 128 || length == 255);
107232153Smm			assertEqualInt(symlinkname[length-1], 'x');
108231200Smm		}
109231200Smm		failure("Found duplicate for %s", pathname);
110231200Smm		assert(strcmp(fns->names[i], pathname) != 0);
111231200Smm		assert((length = strlen(pathname)) <= fns->maxlen);
112231200Smm		if (length > fns->longest_len)
113231200Smm			fns->longest_len = length;
114231200Smm		p = strrchr(pathname, '.');
115231200Smm		if (p != NULL) {
116231200Smm			/* Check a length of file name. */
117231200Smm			assert((size_t)(p - pathname) <= fns->maxflen);
118231200Smm			/* Check a length of file extension. */
119231200Smm			assert(strlen(p+1) <= fns->maxelen);
120231200Smm			if (fns->opt & ONE_DOT) {
121231200Smm				/* Do not have multi dot. */
122231200Smm				assert(strchr(pathname, '.') == p);
123231200Smm			}
124231200Smm		}
125231200Smm		for (p = pathname; *p; p++) {
126231200Smm			if (fns->opt & UPPER_CASE_ONLY) {
127231200Smm				/* Do not have any lower-case character. */
128231200Smm				assert(*p < 'a' || *p > 'z');
129231200Smm			} else
130231200Smm				break;
131231200Smm		}
132231200Smm		if ((fns->opt & ALLOW_LDOT) == 0)
133231200Smm			/* Do not have a dot at the first position. */
134231200Smm			assert(*pathname != '.');
135231200Smm	}
136231200Smm	/* Save the filename which is appeared to use above next time. */
137231200Smm	fns->names[fns->cnt++] = strdup(archive_entry_pathname(ae));
138231200Smm}
139231200Smm
140231200Smmstatic void
141231200Smmverify(unsigned char *buff, size_t used, enum vtype type, struct fns *fns)
142231200Smm{
143231200Smm	struct archive *a;
144231200Smm	struct archive_entry *ae;
145231200Smm	size_t i;
146231200Smm
147231200Smm	/*
148231200Smm	 * Read ISO image.
149231200Smm	 */
150231200Smm	assert((a = archive_read_new()) != NULL);
151231200Smm	assertEqualIntA(a, 0, archive_read_support_format_all(a));
152231200Smm	assertEqualIntA(a, 0, archive_read_support_filter_all(a));
153231200Smm	if (type >= 1)
154231200Smm		assertA(0 == archive_read_set_option(a, NULL, "rockridge",
155231200Smm		    NULL));
156231200Smm	if (type >= 2)
157231200Smm		assertA(0 == archive_read_set_option(a, NULL, "joliet",
158231200Smm		    NULL));
159231200Smm	assertEqualIntA(a, 0, archive_read_open_memory(a, buff, used));
160231200Smm
161231200Smm	/*
162231200Smm	 * Read Root Directory
163231200Smm	 * Root Directory entry must be in ISO image.
164231200Smm	 */
165231200Smm	assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
166231200Smm	assertEqualInt(archive_entry_atime(ae), archive_entry_ctime(ae));
167231200Smm	assertEqualInt(archive_entry_atime(ae), archive_entry_mtime(ae));
168231200Smm	assertEqualString(".", archive_entry_pathname(ae));
169231200Smm	switch (type) {
170231200Smm	case ROCKRIDGE:
171231200Smm		assert((S_IFDIR | 0555) == archive_entry_mode(ae));
172231200Smm		break;
173231200Smm	case JOLIET:
174231200Smm		assert((S_IFDIR | 0700) == archive_entry_mode(ae));
175231200Smm		break;
176231200Smm	case ISO9660:
177231200Smm		assert((S_IFDIR | 0700) == archive_entry_mode(ae));
178231200Smm		break;
179231200Smm	}
180231200Smm
181231200Smm	/*
182231200Smm	 * Verify file status.
183231200Smm	 */
184231200Smm	memset(fns->names, 0, sizeof(char *) * fns->alloc);
185231200Smm	fns->cnt = 0;
186231200Smm	for (i = 0; i < fns->alloc; i++)
187231200Smm		verify_file(a, type, fns);
188231200Smm	for (i = 0; i < fns->alloc; i++)
189231200Smm		free(fns->names[i]);
190231200Smm	assertEqualInt((int)fns->longest_len, (int)fns->maxlen);
191231200Smm
192231200Smm	/*
193231200Smm	 * Verify the end of the archive.
194231200Smm	 */
195231200Smm	assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
196231200Smm	assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
197231200Smm	assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a));
198231200Smm}
199231200Smm
200231200Smmstatic int
201231200Smmcreate_iso_image(unsigned char *buff, size_t buffsize, size_t *used,
202231200Smm    const char *opt)
203231200Smm{
204231200Smm	struct archive *a;
205231200Smm	int i, l, fcnt;
206231200Smm	const int lens[] = {
207231200Smm	    0, 1, 3, 5, 7, 8, 9, 29, 30, 31, 32,
208231200Smm		62, 63, 64, 65, 101, 102, 103, 104,
209231200Smm	    191, 192, 193, 194, 204, 205, 206, 207, 208,
210231200Smm		252, 253, 254, 255,
211231200Smm	    -1 };
212231200Smm	char fname1[256];
213231200Smm	char fname2[256];
214231200Smm	char sym1[2];
215231200Smm	char sym128[129];
216231200Smm	char sym255[256];
217231200Smm
218231200Smm	/* ISO9660 format: Create a new archive in memory. */
219231200Smm	assert((a = archive_write_new()) != NULL);
220231200Smm	assertA(0 == archive_write_set_format_iso9660(a));
221248616Smm	assertA(0 == archive_write_add_filter_none(a));
222231200Smm	assertA(0 == archive_write_set_option(a, NULL, "pad", NULL));
223231200Smm	if (opt)
224231200Smm		assertA(0 == archive_write_set_options(a, opt));
225231200Smm	assertA(0 == archive_write_set_bytes_per_block(a, 1));
226231200Smm	assertA(0 == archive_write_set_bytes_in_last_block(a, 1));
227231200Smm	assertA(0 == archive_write_open_memory(a, buff, buffsize, used));
228231200Smm
229231200Smm	sym1[0] = 'x';
230231200Smm	sym1[1] = '\0';
231232153Smm	for (i = 0; i < (int)sizeof(sym128)-2; i++)
232231200Smm		sym128[i] = 'a';
233231200Smm	sym128[sizeof(sym128)-2] = 'x';
234231200Smm	sym128[sizeof(sym128)-1] = '\0';
235232153Smm	for (i = 0; i < (int)sizeof(sym255)-2; i++)
236231200Smm		sym255[i] = 'a';
237231200Smm	sym255[sizeof(sym255)-2] = 'x';
238231200Smm	sym255[sizeof(sym255)-1] = '\0';
239231200Smm
240231200Smm	fcnt = 0;
241231200Smm	for (i = 0; lens[i] >= 0; i++) {
242231200Smm		for (l = 0; l < lens[i]; l++) {
243231200Smm			fname1[l] = 'a';
244231200Smm			fname2[l] = 'A';
245231200Smm		}
246231200Smm		if (l > 0) {
247231200Smm			fname1[l] = '\0';
248231200Smm			fname2[l] = '\0';
249231200Smm			add_entry(a, fname1, NULL);
250231200Smm			add_entry(a, fname2, sym1);
251231200Smm			fcnt += 2;
252231200Smm		}
253231200Smm		if (l < 254) {
254231200Smm			fname1[l] = '.';
255231200Smm			fname1[l+1] = 'c';
256231200Smm			fname1[l+2] = '\0';
257231200Smm			fname2[l] = '.';
258231200Smm			fname2[l+1] = 'C';
259231200Smm			fname2[l+2] = '\0';
260231200Smm			add_entry(a, fname1, sym128);
261231200Smm			add_entry(a, fname2, sym255);
262231200Smm			fcnt += 2;
263231200Smm		}
264231200Smm		if (l < 252) {
265231200Smm			fname1[l] = '.';
266231200Smm			fname1[l+1] = 'p';
267231200Smm			fname1[l+2] = 'n';
268231200Smm			fname1[l+3] = 'g';
269231200Smm			fname1[l+4] = '\0';
270231200Smm			fname2[l] = '.';
271231200Smm			fname2[l+1] = 'P';
272231200Smm			fname2[l+2] = 'N';
273231200Smm			fname2[l+3] = 'G';
274231200Smm			fname2[l+4] = '\0';
275231200Smm			add_entry(a, fname1, NULL);
276231200Smm			add_entry(a, fname2, sym1);
277231200Smm			fcnt += 2;
278231200Smm		}
279231200Smm		if (l < 251) {
280231200Smm			fname1[l] = '.';
281231200Smm			fname1[l+1] = 'j';
282231200Smm			fname1[l+2] = 'p';
283231200Smm			fname1[l+3] = 'e';
284231200Smm			fname1[l+4] = 'g';
285231200Smm			fname1[l+5] = '\0';
286231200Smm			fname2[l] = '.';
287231200Smm			fname2[l+1] = 'J';
288231200Smm			fname2[l+2] = 'P';
289231200Smm			fname2[l+3] = 'E';
290231200Smm			fname2[l+4] = 'G';
291231200Smm			fname2[l+5] = '\0';
292231200Smm			add_entry(a, fname1, sym128);
293231200Smm			add_entry(a, fname2, sym255);
294231200Smm			fcnt += 2;
295231200Smm		}
296231200Smm	}
297231200Smm
298231200Smm	/* Close out the archive. */
299231200Smm	assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
300231200Smm	assertEqualIntA(a, ARCHIVE_OK, archive_write_free(a));
301231200Smm
302231200Smm	return (fcnt);
303231200Smm}
304231200Smm
305231200SmmDEFINE_TEST(test_write_format_iso9660_filename)
306231200Smm{
307231200Smm	unsigned char *buff;
308231200Smm	size_t buffsize = 120 * 2048;
309231200Smm	size_t used;
310231200Smm	int fcnt;
311231200Smm	struct fns fns;
312231200Smm
313231200Smm	buff = malloc(buffsize);
314231200Smm	assert(buff != NULL);
315248616Smm	if (buff == NULL)
316248616Smm		return;
317231200Smm	memset(&fns, 0, sizeof(fns));
318231200Smm
319231200Smm	/*
320231200Smm	 * Create ISO image with no option.
321231200Smm	 */
322231200Smm	fcnt = create_iso_image(buff, buffsize, &used, NULL);
323231200Smm
324231200Smm	fns.names = (char **)malloc(sizeof(char *) * fcnt);
325231200Smm	assert(fns.names != NULL);
326248616Smm	if (fns.names == NULL) {
327248616Smm		free(buff);
328248616Smm		return;
329248616Smm	}
330231200Smm	fns.alloc = fcnt;
331231200Smm
332231200Smm	/* Verify rockridge filenames. */
333231200Smm	fns.longest_len = 0;
334231200Smm	fns.maxlen = fns.maxflen = fns.maxelen = 255;
335231200Smm	fns.opt = ALLOW_LDOT;
336231200Smm	verify(buff, used, ROCKRIDGE, &fns);
337231200Smm
338231200Smm	/* Verify joliet filenames. */
339231200Smm	fns.longest_len = 0;
340231200Smm	fns.maxlen = fns.maxflen = fns.maxelen = 64;
341231200Smm	fns.opt = ALLOW_LDOT;
342231200Smm	verify(buff, used, JOLIET, &fns);
343231200Smm
344231200Smm	/* Verify ISO9660 filenames. */
345231200Smm	fns.longest_len = 0;
346231200Smm	fns.maxlen = 8+3+1;
347231200Smm	fns.maxflen = 8;
348231200Smm	fns.maxelen = 3;
349231200Smm	fns.opt = UPPER_CASE_ONLY | ONE_DOT;
350231200Smm	verify(buff, used, ISO9660, &fns);
351231200Smm
352231200Smm	/*
353231200Smm	 * Create ISO image with iso-level=2.
354231200Smm	 */
355231200Smm	assertEqualInt(fcnt, create_iso_image(buff, buffsize, &used,
356231200Smm	    "iso-level=2"));
357231200Smm
358231200Smm	/* Verify rockridge filenames. */
359231200Smm	fns.longest_len = 0;
360231200Smm	fns.maxlen = fns.maxflen = fns.maxelen = 255;
361231200Smm	fns.opt = ALLOW_LDOT;
362231200Smm	verify(buff, used, ROCKRIDGE, &fns);
363231200Smm
364231200Smm	/* Verify joliet filenames. */
365231200Smm	fns.longest_len = 0;
366231200Smm	fns.maxlen = fns.maxflen = fns.maxelen = 64;
367231200Smm	fns.opt = ALLOW_LDOT;
368231200Smm	verify(buff, used, JOLIET, &fns);
369231200Smm
370231200Smm	/* Verify ISO9660 filenames. */
371231200Smm	fns.longest_len = 0;
372231200Smm	fns.maxlen = 31;
373231200Smm	fns.maxflen = 30;
374231200Smm	fns.maxelen = 30;
375231200Smm	fns.opt = UPPER_CASE_ONLY | ONE_DOT;
376231200Smm	verify(buff, used, ISO9660, &fns);
377231200Smm
378231200Smm	/*
379231200Smm	 * Create ISO image with iso-level=3.
380231200Smm	 */
381231200Smm	assertEqualInt(fcnt, create_iso_image(buff, buffsize, &used,
382231200Smm	    "iso-level=3"));
383231200Smm
384231200Smm	/* Verify rockridge filenames. */
385231200Smm	fns.longest_len = 0;
386231200Smm	fns.maxlen = fns.maxflen = fns.maxelen = 255;
387231200Smm	fns.opt = ALLOW_LDOT;
388231200Smm	verify(buff, used, ROCKRIDGE, &fns);
389231200Smm
390231200Smm	/* Verify joliet filenames. */
391231200Smm	fns.longest_len = 0;
392231200Smm	fns.maxlen = fns.maxflen = fns.maxelen = 64;
393231200Smm	fns.opt = ALLOW_LDOT;
394231200Smm	verify(buff, used, JOLIET, &fns);
395231200Smm
396231200Smm	/* Verify ISO9660 filenames. */
397231200Smm	fns.longest_len = 0;
398231200Smm	fns.maxlen = 31;
399231200Smm	fns.maxflen = 30;
400231200Smm	fns.maxelen = 30;
401231200Smm	fns.opt = UPPER_CASE_ONLY | ONE_DOT;
402231200Smm	verify(buff, used, ISO9660, &fns);
403231200Smm
404231200Smm	/*
405231200Smm	 * Create ISO image with iso-level=4.
406231200Smm	 */
407231200Smm	assertEqualInt(fcnt, create_iso_image(buff, buffsize, &used,
408231200Smm	    "iso-level=4"));
409231200Smm
410231200Smm	/* Verify rockridge filenames. */
411231200Smm	fns.longest_len = 0;
412231200Smm	fns.maxlen = fns.maxflen = fns.maxelen = 255;
413231200Smm	fns.opt = ALLOW_LDOT;
414231200Smm	verify(buff, used, ROCKRIDGE, &fns);
415231200Smm
416231200Smm	/* Verify joliet filenames. */
417231200Smm	fns.longest_len = 0;
418231200Smm	fns.maxlen = fns.maxflen = fns.maxelen = 64;
419231200Smm	fns.opt = ALLOW_LDOT;
420231200Smm	verify(buff, used, JOLIET, &fns);
421231200Smm
422231200Smm	/* Verify ISO9660 filenames. */
423231200Smm	fns.longest_len = 0;
424231200Smm	fns.maxlen = fns.maxflen = fns.maxelen = 193;
425231200Smm	fns.opt = ALLOW_LDOT;
426231200Smm	verify(buff, used, ISO9660, &fns);
427231200Smm
428231200Smm	/*
429231200Smm	 * Create ISO image with iso-level=4 and !rockridge.
430231200Smm	 */
431231200Smm	assertEqualInt(fcnt, create_iso_image(buff, buffsize, &used,
432231200Smm	    "iso-level=4,!rockridge"));
433231200Smm
434231200Smm	/* Verify joliet filenames. */
435231200Smm	fns.longest_len = 0;
436231200Smm	fns.maxlen = fns.maxflen = fns.maxelen = 64;
437231200Smm	fns.opt = ALLOW_LDOT;
438231200Smm	verify(buff, used, JOLIET, &fns);
439231200Smm
440231200Smm	/* Verify ISO9660 filenames. */
441231200Smm	fns.longest_len = 0;
442231200Smm	fns.maxlen = fns.maxflen = fns.maxelen = 207;
443231200Smm	fns.opt = ALLOW_LDOT;
444231200Smm	verify(buff, used, ISO9660, &fns);
445231200Smm
446231200Smm	/*
447231200Smm	 * Create ISO image with joliet=long.
448231200Smm	 */
449231200Smm	assertEqualInt(fcnt, create_iso_image(buff, buffsize, &used,
450231200Smm	    "joliet=long"));
451231200Smm
452231200Smm	/* Verify rockridge filenames. */
453231200Smm	fns.longest_len = 0;
454231200Smm	fns.maxlen = fns.maxflen = fns.maxelen = 255;
455231200Smm	fns.opt = ALLOW_LDOT;
456231200Smm	verify(buff, used, ROCKRIDGE, &fns);
457231200Smm
458231200Smm	/* Verify joliet filenames. */
459231200Smm	fns.longest_len = 0;
460231200Smm	fns.maxlen = fns.maxflen = fns.maxelen = 103;
461231200Smm	fns.opt = ALLOW_LDOT;
462231200Smm	verify(buff, used, JOLIET, &fns);
463231200Smm
464231200Smm	/* Verify ISO9660 filenames. */
465231200Smm	fns.longest_len = 0;
466231200Smm	fns.maxlen = 8+3+1;
467231200Smm	fns.maxflen = 8;
468231200Smm	fns.maxelen = 3;
469231200Smm	fns.opt = UPPER_CASE_ONLY | ONE_DOT;
470231200Smm	verify(buff, used, ISO9660, &fns);
471231200Smm
472231200Smm	free(fns.names);
473231200Smm	free(buff);
474231200Smm}
475