test_write_format_iso9660_filename.c revision 231200
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 *symlink = archive_entry_symlink(ae);
102		size_t length;
103
104		if (symlink != NULL) {
105			length = strlen(symlink);
106			assert(length == 1 || length == 128 || length == 255);
107			assertEqualInt(symlink[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_set_compression_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 < 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 < 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	memset(&fns, 0, sizeof(fns));
316
317	/*
318	 * Create ISO image with no option.
319	 */
320	fcnt = create_iso_image(buff, buffsize, &used, NULL);
321
322	fns.names = (char **)malloc(sizeof(char *) * fcnt);
323	assert(fns.names != NULL);
324	fns.alloc = fcnt;
325
326	/* Verify rockridge filenames. */
327	fns.longest_len = 0;
328	fns.maxlen = fns.maxflen = fns.maxelen = 255;
329	fns.opt = ALLOW_LDOT;
330	verify(buff, used, ROCKRIDGE, &fns);
331
332	/* Verify joliet filenames. */
333	fns.longest_len = 0;
334	fns.maxlen = fns.maxflen = fns.maxelen = 64;
335	fns.opt = ALLOW_LDOT;
336	verify(buff, used, JOLIET, &fns);
337
338	/* Verify ISO9660 filenames. */
339	fns.longest_len = 0;
340	fns.maxlen = 8+3+1;
341	fns.maxflen = 8;
342	fns.maxelen = 3;
343	fns.opt = UPPER_CASE_ONLY | ONE_DOT;
344	verify(buff, used, ISO9660, &fns);
345
346	/*
347	 * Create ISO image with iso-level=2.
348	 */
349	assertEqualInt(fcnt, create_iso_image(buff, buffsize, &used,
350	    "iso-level=2"));
351
352	/* Verify rockridge filenames. */
353	fns.longest_len = 0;
354	fns.maxlen = fns.maxflen = fns.maxelen = 255;
355	fns.opt = ALLOW_LDOT;
356	verify(buff, used, ROCKRIDGE, &fns);
357
358	/* Verify joliet filenames. */
359	fns.longest_len = 0;
360	fns.maxlen = fns.maxflen = fns.maxelen = 64;
361	fns.opt = ALLOW_LDOT;
362	verify(buff, used, JOLIET, &fns);
363
364	/* Verify ISO9660 filenames. */
365	fns.longest_len = 0;
366	fns.maxlen = 31;
367	fns.maxflen = 30;
368	fns.maxelen = 30;
369	fns.opt = UPPER_CASE_ONLY | ONE_DOT;
370	verify(buff, used, ISO9660, &fns);
371
372	/*
373	 * Create ISO image with iso-level=3.
374	 */
375	assertEqualInt(fcnt, create_iso_image(buff, buffsize, &used,
376	    "iso-level=3"));
377
378	/* Verify rockridge filenames. */
379	fns.longest_len = 0;
380	fns.maxlen = fns.maxflen = fns.maxelen = 255;
381	fns.opt = ALLOW_LDOT;
382	verify(buff, used, ROCKRIDGE, &fns);
383
384	/* Verify joliet filenames. */
385	fns.longest_len = 0;
386	fns.maxlen = fns.maxflen = fns.maxelen = 64;
387	fns.opt = ALLOW_LDOT;
388	verify(buff, used, JOLIET, &fns);
389
390	/* Verify ISO9660 filenames. */
391	fns.longest_len = 0;
392	fns.maxlen = 31;
393	fns.maxflen = 30;
394	fns.maxelen = 30;
395	fns.opt = UPPER_CASE_ONLY | ONE_DOT;
396	verify(buff, used, ISO9660, &fns);
397
398	/*
399	 * Create ISO image with iso-level=4.
400	 */
401	assertEqualInt(fcnt, create_iso_image(buff, buffsize, &used,
402	    "iso-level=4"));
403
404	/* Verify rockridge filenames. */
405	fns.longest_len = 0;
406	fns.maxlen = fns.maxflen = fns.maxelen = 255;
407	fns.opt = ALLOW_LDOT;
408	verify(buff, used, ROCKRIDGE, &fns);
409
410	/* Verify joliet filenames. */
411	fns.longest_len = 0;
412	fns.maxlen = fns.maxflen = fns.maxelen = 64;
413	fns.opt = ALLOW_LDOT;
414	verify(buff, used, JOLIET, &fns);
415
416	/* Verify ISO9660 filenames. */
417	fns.longest_len = 0;
418	fns.maxlen = fns.maxflen = fns.maxelen = 193;
419	fns.opt = ALLOW_LDOT;
420	verify(buff, used, ISO9660, &fns);
421
422	/*
423	 * Create ISO image with iso-level=4 and !rockridge.
424	 */
425	assertEqualInt(fcnt, create_iso_image(buff, buffsize, &used,
426	    "iso-level=4,!rockridge"));
427
428	/* Verify joliet filenames. */
429	fns.longest_len = 0;
430	fns.maxlen = fns.maxflen = fns.maxelen = 64;
431	fns.opt = ALLOW_LDOT;
432	verify(buff, used, JOLIET, &fns);
433
434	/* Verify ISO9660 filenames. */
435	fns.longest_len = 0;
436	fns.maxlen = fns.maxflen = fns.maxelen = 207;
437	fns.opt = ALLOW_LDOT;
438	verify(buff, used, ISO9660, &fns);
439
440	/*
441	 * Create ISO image with joliet=long.
442	 */
443	assertEqualInt(fcnt, create_iso_image(buff, buffsize, &used,
444	    "joliet=long"));
445
446	/* Verify rockridge filenames. */
447	fns.longest_len = 0;
448	fns.maxlen = fns.maxflen = fns.maxelen = 255;
449	fns.opt = ALLOW_LDOT;
450	verify(buff, used, ROCKRIDGE, &fns);
451
452	/* Verify joliet filenames. */
453	fns.longest_len = 0;
454	fns.maxlen = fns.maxflen = fns.maxelen = 103;
455	fns.opt = ALLOW_LDOT;
456	verify(buff, used, JOLIET, &fns);
457
458	/* Verify ISO9660 filenames. */
459	fns.longest_len = 0;
460	fns.maxlen = 8+3+1;
461	fns.maxflen = 8;
462	fns.maxelen = 3;
463	fns.opt = UPPER_CASE_ONLY | ONE_DOT;
464	verify(buff, used, ISO9660, &fns);
465
466	free(fns.names);
467	free(buff);
468}
469