xref: /freebsd/contrib/libarchive/tar/creation_set.c (revision 95eb4b873b6a8b527c5bd78d7191975dfca38998)
1 /*-
2  * Copyright (c) 2012 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 
26 #include "bsdtar_platform.h"
27 
28 #ifdef HAVE_STDLIB_H
29 #include <stdlib.h>
30 #endif
31 #ifdef HAVE_STRING_H
32 #include <string.h>
33 #endif
34 
35 #include "bsdtar.h"
36 #include "err.h"
37 
38 struct creation_set {
39 	char		 *create_format;
40 	struct filter_set {
41 		int	  program;	/* Set 1 if filter is a program name */
42 		char	 *filter_name;
43 	}		 *filters;
44 	int		  filter_count;
45 };
46 
47 struct suffix_code_t {
48 	const char *suffix;
49 	const char *form;
50 };
51 
52 static const char *
53 get_suffix_code(const struct suffix_code_t *tbl, const char *suffix)
54 {
55 	int i;
56 
57 	if (suffix == NULL)
58 		return (NULL);
59 	for (i = 0; tbl[i].suffix != NULL; i++) {
60 		if (strcmp(tbl[i].suffix, suffix) == 0)
61 			return (tbl[i].form);
62 	}
63 	return (NULL);
64 }
65 
66 static const char *
67 get_filter_code(const char *suffix)
68 {
69 	/* A pair of suffix and compression/filter. */
70 	static const struct suffix_code_t filters[] = {
71 		{ ".Z",		"compress" },
72 		{ ".bz2",	"bzip2" },
73 		{ ".gz",	"gzip" },
74 		{ ".grz",	"grzip" },
75 		{ ".lrz",	"lrzip" },
76 		{ ".lz",	"lzip" },
77 		{ ".lz4",	"lz4" },
78 		{ ".lzo",	"lzop" },
79 		{ ".lzma",	"lzma" },
80 		{ ".uu",	"uuencode" },
81 		{ ".xz",	"xz" },
82 		{ ".zst",	"zstd"},
83 		{ NULL,		NULL }
84 	};
85 
86 	return get_suffix_code(filters, suffix);
87 }
88 
89 static const char *
90 get_format_code(const char *suffix)
91 {
92 	/* A pair of suffix and format. */
93 	static const struct suffix_code_t formats[] = {
94 		{ ".7z",	"7zip" },
95 		{ ".ar",	"arbsd" },
96 		{ ".cpio",	"cpio" },
97 		{ ".iso",	"iso9660" },
98 		{ ".mtree",	"mtree" },
99 		{ ".shar",	"shar" },
100 		{ ".tar",	"paxr" },
101 		{ ".warc",	"warc" },
102 		{ ".xar",	"xar" },
103 		{ ".zip",	"zip" },
104 		{ NULL,		NULL }
105 	};
106 
107 	return get_suffix_code(formats, suffix);
108 }
109 
110 static const char *
111 decompose_alias(const char *suffix)
112 {
113 	static const struct suffix_code_t alias[] = {
114 		{ ".taz",	".tar.gz" },
115 		{ ".tgz",	".tar.gz" },
116 		{ ".tbz",	".tar.bz2" },
117 		{ ".tbz2",	".tar.bz2" },
118 		{ ".tz2",	".tar.bz2" },
119 		{ ".tlz",	".tar.lzma" },
120 		{ ".txz",	".tar.xz" },
121 		{ ".tzo",	".tar.lzo" },
122 		{ ".taZ",	".tar.Z" },
123 		{ ".tZ",	".tar.Z" },
124 		{ ".tzst",	".tar.zst" },
125 		{ NULL,		NULL }
126 	};
127 
128 	return get_suffix_code(alias, suffix);
129 }
130 
131 static void
132 _cset_add_filter(struct creation_set *cset, int program, const char *filter)
133 {
134 	struct filter_set *new_ptr;
135 	char *new_filter;
136 
137 	new_ptr = (struct filter_set *)realloc(cset->filters,
138 	    sizeof(*cset->filters) * (cset->filter_count + 1));
139 	if (new_ptr == NULL)
140 		lafe_errc(1, 0, "No memory");
141 	new_filter = strdup(filter);
142 	if (new_filter == NULL)
143 		lafe_errc(1, 0, "No memory");
144 	cset->filters = new_ptr;
145 	cset->filters[cset->filter_count].program = program;
146 	cset->filters[cset->filter_count].filter_name = new_filter;
147 	cset->filter_count++;
148 }
149 
150 void
151 cset_add_filter(struct creation_set *cset, const char *filter)
152 {
153 	_cset_add_filter(cset, 0, filter);
154 }
155 
156 void
157 cset_add_filter_program(struct creation_set *cset, const char *filter)
158 {
159 	_cset_add_filter(cset, 1, filter);
160 }
161 
162 int
163 cset_read_support_filter_program(struct creation_set *cset, struct archive *a)
164 {
165 	int cnt = 0, i;
166 
167 	for (i = 0; i < cset->filter_count; i++) {
168 		if (cset->filters[i].program) {
169 			archive_read_support_filter_program(a,
170 			    cset->filters[i].filter_name);
171 			++cnt;
172 		}
173 	}
174 	return (cnt);
175 }
176 
177 int
178 cset_write_add_filters(struct creation_set *cset, struct archive *a,
179     const void **filter_name)
180 {
181 	int cnt = 0, i, r;
182 
183 	for (i = 0; i < cset->filter_count; i++) {
184 		if (cset->filters[i].program)
185 			r = archive_write_add_filter_program(a,
186 				cset->filters[i].filter_name);
187 		else
188 			r = archive_write_add_filter_by_name(a,
189 				cset->filters[i].filter_name);
190 		if (r < ARCHIVE_WARN) {
191 			*filter_name = cset->filters[i].filter_name;
192 			return (r);
193 		}
194 		++cnt;
195 	}
196 	return (cnt);
197 }
198 
199 void
200 cset_set_format(struct creation_set *cset, const char *format)
201 {
202 	char *f;
203 
204 	f = strdup(format);
205 	if (f == NULL)
206 		lafe_errc(1, 0, "No memory");
207 	free(cset->create_format);
208 	cset->create_format = f;
209 }
210 
211 const char *
212 cset_get_format(struct creation_set *cset)
213 {
214 	return (cset->create_format);
215 }
216 
217 static void
218 _cleanup_filters(struct filter_set *filters, int count)
219 {
220 	int i;
221 
222 	for (i = 0; i < count; i++)
223 		free(filters[i].filter_name);
224 	free(filters);
225 }
226 
227 /*
228  * Clean up a creation set.
229  */
230 void
231 cset_free(struct creation_set *cset)
232 {
233 	_cleanup_filters(cset->filters, cset->filter_count);
234 	free(cset->create_format);
235 	free(cset);
236 }
237 
238 struct creation_set *
239 cset_new(void)
240 {
241 	return calloc(1, sizeof(struct creation_set));
242 }
243 
244 /*
245  * Build a creation set by a file name suffix.
246  */
247 int
248 cset_auto_compress(struct creation_set *cset, const char *filename)
249 {
250 	struct filter_set *old_filters;
251 	char *name, *p;
252 	const char *code;
253 	int old_filter_count;
254 
255 	name = strdup(filename);
256 	if (name == NULL)
257 		lafe_errc(1, 0, "No memory");
258 	/* Save previous filters. */
259 	old_filters = cset->filters;
260 	old_filter_count = cset->filter_count;
261 	cset->filters = NULL;
262 	cset->filter_count = 0;
263 
264 	for (;;) {
265 		/* Get the suffix. */
266 		p = strrchr(name, '.');
267 		if (p == NULL)
268 			break;
269 		/* Suppose it indicates compression/filter type
270 		 * such as ".gz". */
271 		code = get_filter_code(p);
272 		if (code != NULL) {
273 			cset_add_filter(cset, code);
274 			*p = '\0';
275 			continue;
276 		}
277 		/* Suppose it indicates format type such as ".tar". */
278 		code = get_format_code(p);
279 		if (code != NULL) {
280 			cset_set_format(cset, code);
281 			break;
282 		}
283 		/* Suppose it indicates alias such as ".tgz". */
284 		code = decompose_alias(p);
285 		if (code == NULL)
286 			break;
287 		/* Replace the suffix. */
288 		*p = '\0';
289 		name = realloc(name, strlen(name) + strlen(code) + 1);
290 		if (name == NULL)
291 			lafe_errc(1, 0, "No memory");
292 		strcat(name, code);
293 	}
294 	free(name);
295 	if (cset->filters) {
296 		struct filter_set *v;
297 		int i, r;
298 
299 		/* Release previous filters. */
300 		_cleanup_filters(old_filters, old_filter_count);
301 
302 		v = malloc(sizeof(*v) * cset->filter_count);
303 		if (v == NULL)
304 			lafe_errc(1, 0, "No memory");
305 		/* Reverse filter sequence. */
306 		for (i = 0, r = cset->filter_count; r > 0; )
307 			v[i++] = cset->filters[--r];
308 		free(cset->filters);
309 		cset->filters = v;
310 		return (1);
311 	} else {
312 		/* Put previous filters back. */
313 		cset->filters = old_filters;
314 		cset->filter_count = old_filter_count;
315 		return (0);
316 	}
317 }
318