archive_write_set_format_shar.c revision 358088
1/*-
2 * Copyright (c) 2003-2007 Tim Kientzle
3 * Copyright (c) 2008 Joerg Sonnenberger
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "archive_platform.h"
28__FBSDID("$FreeBSD: stable/11/contrib/libarchive/libarchive/archive_write_set_format_shar.c 358088 2020-02-19 01:50:47Z mm $");
29
30#ifdef HAVE_ERRNO_H
31#include <errno.h>
32#endif
33#include <stdio.h>
34#ifdef HAVE_STDLIB_H
35#include <stdlib.h>
36#endif
37#ifdef HAVE_STRING_H
38#include <string.h>
39#endif
40
41#include "archive.h"
42#include "archive_entry.h"
43#include "archive_private.h"
44#include "archive_write_private.h"
45#include "archive_write_set_format_private.h"
46
47struct shar {
48	int			 dump;
49	int			 end_of_line;
50	struct archive_entry	*entry;
51	int			 has_data;
52	char			*last_dir;
53
54	/* Line buffer for uuencoded dump format */
55	char			 outbuff[45];
56	size_t			 outpos;
57
58	int			 wrote_header;
59	struct archive_string	 work;
60	struct archive_string	 quoted_name;
61};
62
63static int	archive_write_shar_close(struct archive_write *);
64static int	archive_write_shar_free(struct archive_write *);
65static int	archive_write_shar_header(struct archive_write *,
66		    struct archive_entry *);
67static ssize_t	archive_write_shar_data_sed(struct archive_write *,
68		    const void * buff, size_t);
69static ssize_t	archive_write_shar_data_uuencode(struct archive_write *,
70		    const void * buff, size_t);
71static int	archive_write_shar_finish_entry(struct archive_write *);
72
73/*
74 * Copy the given string to the buffer, quoting all shell meta characters
75 * found.
76 */
77static void
78shar_quote(struct archive_string *buf, const char *str, int in_shell)
79{
80	static const char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~";
81	size_t len;
82
83	while (*str != '\0') {
84		if ((len = strcspn(str, meta)) != 0) {
85			archive_strncat(buf, str, len);
86			str += len;
87		} else if (*str == '\n') {
88			if (in_shell)
89				archive_strcat(buf, "\"\n\"");
90			else
91				archive_strcat(buf, "\\n");
92			++str;
93		} else {
94			archive_strappend_char(buf, '\\');
95			archive_strappend_char(buf, *str);
96			++str;
97		}
98	}
99}
100
101/*
102 * Set output format to 'shar' format.
103 */
104int
105archive_write_set_format_shar(struct archive *_a)
106{
107	struct archive_write *a = (struct archive_write *)_a;
108	struct shar *shar;
109
110	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
111	    ARCHIVE_STATE_NEW, "archive_write_set_format_shar");
112
113	/* If someone else was already registered, unregister them. */
114	if (a->format_free != NULL)
115		(a->format_free)(a);
116
117	shar = (struct shar *)calloc(1, sizeof(*shar));
118	if (shar == NULL) {
119		archive_set_error(&a->archive, ENOMEM, "Can't allocate shar data");
120		return (ARCHIVE_FATAL);
121	}
122	archive_string_init(&shar->work);
123	archive_string_init(&shar->quoted_name);
124	a->format_data = shar;
125	a->format_name = "shar";
126	a->format_write_header = archive_write_shar_header;
127	a->format_close = archive_write_shar_close;
128	a->format_free = archive_write_shar_free;
129	a->format_write_data = archive_write_shar_data_sed;
130	a->format_finish_entry = archive_write_shar_finish_entry;
131	a->archive.archive_format = ARCHIVE_FORMAT_SHAR_BASE;
132	a->archive.archive_format_name = "shar";
133	return (ARCHIVE_OK);
134}
135
136/*
137 * An alternate 'shar' that uses uudecode instead of 'sed' to encode
138 * file contents and can therefore be used to archive binary files.
139 * In addition, this variant also attempts to restore ownership, file modes,
140 * and other extended file information.
141 */
142int
143archive_write_set_format_shar_dump(struct archive *_a)
144{
145	struct archive_write *a = (struct archive_write *)_a;
146	struct shar *shar;
147
148	archive_write_set_format_shar(&a->archive);
149	shar = (struct shar *)a->format_data;
150	shar->dump = 1;
151	a->format_write_data = archive_write_shar_data_uuencode;
152	a->archive.archive_format = ARCHIVE_FORMAT_SHAR_DUMP;
153	a->archive.archive_format_name = "shar dump";
154	return (ARCHIVE_OK);
155}
156
157static int
158archive_write_shar_header(struct archive_write *a, struct archive_entry *entry)
159{
160	const char *linkname;
161	const char *name;
162	char *p, *pp;
163	struct shar *shar;
164
165	shar = (struct shar *)a->format_data;
166	if (!shar->wrote_header) {
167		archive_strcat(&shar->work, "#!/bin/sh\n");
168		archive_strcat(&shar->work, "# This is a shell archive\n");
169		shar->wrote_header = 1;
170	}
171
172	/* Save the entry for the closing. */
173	archive_entry_free(shar->entry);
174	shar->entry = archive_entry_clone(entry);
175	name = archive_entry_pathname(entry);
176
177	/* Handle some preparatory issues. */
178	switch(archive_entry_filetype(entry)) {
179	case AE_IFREG:
180		/* Only regular files have non-zero size. */
181		break;
182	case AE_IFDIR:
183		archive_entry_set_size(entry, 0);
184		/* Don't bother trying to recreate '.' */
185		if (strcmp(name, ".") == 0  ||  strcmp(name, "./") == 0)
186			return (ARCHIVE_OK);
187		break;
188	case AE_IFIFO:
189	case AE_IFCHR:
190	case AE_IFBLK:
191		/* All other file types have zero size in the archive. */
192		archive_entry_set_size(entry, 0);
193		break;
194	default:
195		archive_entry_set_size(entry, 0);
196		if (archive_entry_hardlink(entry) == NULL &&
197		    archive_entry_symlink(entry) == NULL) {
198			__archive_write_entry_filetype_unsupported(
199			    &a->archive, entry, "shar");
200			return (ARCHIVE_WARN);
201		}
202	}
203
204	archive_string_empty(&shar->quoted_name);
205	shar_quote(&shar->quoted_name, name, 1);
206
207	/* Stock preparation for all file types. */
208	archive_string_sprintf(&shar->work, "echo x %s\n", shar->quoted_name.s);
209
210	if (archive_entry_filetype(entry) != AE_IFDIR) {
211		/* Try to create the dir. */
212		p = strdup(name);
213		pp = strrchr(p, '/');
214		/* If there is a / character, try to create the dir. */
215		if (pp != NULL) {
216			*pp = '\0';
217
218			/* Try to avoid a lot of redundant mkdir commands. */
219			if (strcmp(p, ".") == 0) {
220				/* Don't try to "mkdir ." */
221				free(p);
222			} else if (shar->last_dir == NULL) {
223				archive_strcat(&shar->work, "mkdir -p ");
224				shar_quote(&shar->work, p, 1);
225				archive_strcat(&shar->work,
226				    " > /dev/null 2>&1\n");
227				shar->last_dir = p;
228			} else if (strcmp(p, shar->last_dir) == 0) {
229				/* We've already created this exact dir. */
230				free(p);
231			} else if (strlen(p) < strlen(shar->last_dir) &&
232			    strncmp(p, shar->last_dir, strlen(p)) == 0) {
233				/* We've already created a subdir. */
234				free(p);
235			} else {
236				archive_strcat(&shar->work, "mkdir -p ");
237				shar_quote(&shar->work, p, 1);
238				archive_strcat(&shar->work,
239				    " > /dev/null 2>&1\n");
240				shar->last_dir = p;
241			}
242		} else {
243			free(p);
244		}
245	}
246
247	/* Handle file-type specific issues. */
248	shar->has_data = 0;
249	if ((linkname = archive_entry_hardlink(entry)) != NULL) {
250		archive_strcat(&shar->work, "ln -f ");
251		shar_quote(&shar->work, linkname, 1);
252		archive_string_sprintf(&shar->work, " %s\n",
253		    shar->quoted_name.s);
254	} else if ((linkname = archive_entry_symlink(entry)) != NULL) {
255		archive_strcat(&shar->work, "ln -fs ");
256		shar_quote(&shar->work, linkname, 1);
257		archive_string_sprintf(&shar->work, " %s\n",
258		    shar->quoted_name.s);
259	} else {
260		switch(archive_entry_filetype(entry)) {
261		case AE_IFREG:
262			if (archive_entry_size(entry) == 0) {
263				/* More portable than "touch." */
264				archive_string_sprintf(&shar->work,
265				    "test -e \"%s\" || :> \"%s\"\n",
266				    shar->quoted_name.s, shar->quoted_name.s);
267			} else {
268				if (shar->dump) {
269					unsigned int mode = archive_entry_mode(entry) & 0777;
270					archive_string_sprintf(&shar->work,
271					    "uudecode -p > %s << 'SHAR_END'\n",
272					    shar->quoted_name.s);
273					archive_string_sprintf(&shar->work,
274					    "begin %o ", mode);
275					shar_quote(&shar->work, name, 0);
276					archive_strcat(&shar->work, "\n");
277				} else {
278					archive_string_sprintf(&shar->work,
279					    "sed 's/^X//' > %s << 'SHAR_END'\n",
280					    shar->quoted_name.s);
281				}
282				shar->has_data = 1;
283				shar->end_of_line = 1;
284				shar->outpos = 0;
285			}
286			break;
287		case AE_IFDIR:
288			archive_string_sprintf(&shar->work,
289			    "mkdir -p %s > /dev/null 2>&1\n",
290			    shar->quoted_name.s);
291			/* Record that we just created this directory. */
292			free(shar->last_dir);
293
294			shar->last_dir = strdup(name);
295			/* Trim a trailing '/'. */
296			pp = strrchr(shar->last_dir, '/');
297			if (pp != NULL && pp[1] == '\0')
298				*pp = '\0';
299			/*
300			 * TODO: Put dir name/mode on a list to be fixed
301			 * up at end of archive.
302			 */
303			break;
304		case AE_IFIFO:
305			archive_string_sprintf(&shar->work,
306			    "mkfifo %s\n", shar->quoted_name.s);
307			break;
308		case AE_IFCHR:
309			archive_string_sprintf(&shar->work,
310			    "mknod %s c %ju %ju\n", shar->quoted_name.s,
311			    (uintmax_t)archive_entry_rdevmajor(entry),
312			    (uintmax_t)archive_entry_rdevminor(entry));
313			break;
314		case AE_IFBLK:
315			archive_string_sprintf(&shar->work,
316			    "mknod %s b %ju %ju\n", shar->quoted_name.s,
317			    (uintmax_t)archive_entry_rdevmajor(entry),
318			    (uintmax_t)archive_entry_rdevminor(entry));
319			break;
320		default:
321			return (ARCHIVE_WARN);
322		}
323	}
324
325	return (ARCHIVE_OK);
326}
327
328static ssize_t
329archive_write_shar_data_sed(struct archive_write *a, const void *buff, size_t n)
330{
331	static const size_t ensured = 65533;
332	struct shar *shar;
333	const char *src;
334	char *buf, *buf_end;
335	int ret;
336	size_t written = n;
337
338	shar = (struct shar *)a->format_data;
339	if (!shar->has_data || n == 0)
340		return (0);
341
342	src = (const char *)buff;
343
344	/*
345	 * ensure is the number of bytes in buffer before expanding the
346	 * current character.  Each operation writes the current character
347	 * and optionally the start-of-new-line marker.  This can happen
348	 * twice before entering the loop, so make sure three additional
349	 * bytes can be written.
350	 */
351	if (archive_string_ensure(&shar->work, ensured + 3) == NULL) {
352		archive_set_error(&a->archive, ENOMEM, "Out of memory");
353		return (ARCHIVE_FATAL);
354	}
355
356	if (shar->work.length > ensured) {
357		ret = __archive_write_output(a, shar->work.s,
358		    shar->work.length);
359		if (ret != ARCHIVE_OK)
360			return (ARCHIVE_FATAL);
361		archive_string_empty(&shar->work);
362	}
363	buf = shar->work.s + shar->work.length;
364	buf_end = shar->work.s + ensured;
365
366	if (shar->end_of_line) {
367		*buf++ = 'X';
368		shar->end_of_line = 0;
369	}
370
371	while (n-- != 0) {
372		if ((*buf++ = *src++) == '\n') {
373			if (n == 0)
374				shar->end_of_line = 1;
375			else
376				*buf++ = 'X';
377		}
378
379		if (buf >= buf_end) {
380			shar->work.length = buf - shar->work.s;
381			ret = __archive_write_output(a, shar->work.s,
382			    shar->work.length);
383			if (ret != ARCHIVE_OK)
384				return (ARCHIVE_FATAL);
385			archive_string_empty(&shar->work);
386			buf = shar->work.s;
387		}
388	}
389
390	shar->work.length = buf - shar->work.s;
391
392	return (written);
393}
394
395#define	UUENC(c)	(((c)!=0) ? ((c) & 077) + ' ': '`')
396
397static void
398uuencode_group(const char _in[3], char out[4])
399{
400	const unsigned char *in = (const unsigned char *)_in;
401	int t;
402
403	t = (in[0] << 16) | (in[1] << 8) | in[2];
404	out[0] = UUENC( 0x3f & (t >> 18) );
405	out[1] = UUENC( 0x3f & (t >> 12) );
406	out[2] = UUENC( 0x3f & (t >> 6) );
407	out[3] = UUENC( 0x3f & t );
408}
409
410static int
411_uuencode_line(struct archive_write *a, struct shar *shar, const char *inbuf, size_t len)
412{
413	char *buf;
414	size_t alloc_len;
415
416	/* len <= 45 -> expanded to 60 + len byte + new line */
417	alloc_len = shar->work.length + 62;
418	if (archive_string_ensure(&shar->work, alloc_len) == NULL) {
419		archive_set_error(&a->archive, ENOMEM, "Out of memory");
420		return (ARCHIVE_FATAL);
421	}
422
423	buf = shar->work.s + shar->work.length;
424	*buf++ = UUENC(len);
425	while (len >= 3) {
426		uuencode_group(inbuf, buf);
427		len -= 3;
428		inbuf += 3;
429		buf += 4;
430	}
431	if (len != 0) {
432		char tmp_buf[3];
433		tmp_buf[0] = inbuf[0];
434		if (len == 1)
435			tmp_buf[1] = '\0';
436		else
437			tmp_buf[1] = inbuf[1];
438		tmp_buf[2] = '\0';
439		uuencode_group(tmp_buf, buf);
440		buf += 4;
441	}
442	*buf++ = '\n';
443	if ((buf - shar->work.s) > (ptrdiff_t)(shar->work.length + 62)) {
444		archive_set_error(&a->archive,
445		    ARCHIVE_ERRNO_MISC, "Buffer overflow");
446		return (ARCHIVE_FATAL);
447	}
448	shar->work.length = buf - shar->work.s;
449	return (ARCHIVE_OK);
450}
451
452#define uuencode_line(__a, __shar, __inbuf, __len) \
453	do { \
454		int r = _uuencode_line(__a, __shar, __inbuf, __len); \
455		if (r != ARCHIVE_OK) \
456			return (ARCHIVE_FATAL); \
457	} while (0)
458
459static ssize_t
460archive_write_shar_data_uuencode(struct archive_write *a, const void *buff,
461    size_t length)
462{
463	struct shar *shar;
464	const char *src;
465	size_t n;
466	int ret;
467
468	shar = (struct shar *)a->format_data;
469	if (!shar->has_data)
470		return (ARCHIVE_OK);
471	src = (const char *)buff;
472
473	if (shar->outpos != 0) {
474		n = 45 - shar->outpos;
475		if (n > length)
476			n = length;
477		memcpy(shar->outbuff + shar->outpos, src, n);
478		if (shar->outpos + n < 45) {
479			shar->outpos += n;
480			return length;
481		}
482		uuencode_line(a, shar, shar->outbuff, 45);
483		src += n;
484		n = length - n;
485	} else {
486		n = length;
487	}
488
489	while (n >= 45) {
490		uuencode_line(a, shar, src, 45);
491		src += 45;
492		n -= 45;
493
494		if (shar->work.length < 65536)
495			continue;
496		ret = __archive_write_output(a, shar->work.s,
497		    shar->work.length);
498		if (ret != ARCHIVE_OK)
499			return (ARCHIVE_FATAL);
500		archive_string_empty(&shar->work);
501	}
502	if (n != 0) {
503		memcpy(shar->outbuff, src, n);
504		shar->outpos = n;
505	}
506	return (length);
507}
508
509static int
510archive_write_shar_finish_entry(struct archive_write *a)
511{
512	const char *g, *p, *u;
513	struct shar *shar;
514	int ret;
515
516	shar = (struct shar *)a->format_data;
517	if (shar->entry == NULL)
518		return (0);
519
520	if (shar->dump) {
521		/* Finish uuencoded data. */
522		if (shar->has_data) {
523			if (shar->outpos > 0)
524				uuencode_line(a, shar, shar->outbuff,
525				    shar->outpos);
526			archive_strcat(&shar->work, "`\nend\n");
527			archive_strcat(&shar->work, "SHAR_END\n");
528		}
529		/* Restore file mode, owner, flags. */
530		/*
531		 * TODO: Don't immediately restore mode for
532		 * directories; defer that to end of script.
533		 */
534		archive_string_sprintf(&shar->work, "chmod %o ",
535		    (unsigned int)(archive_entry_mode(shar->entry) & 07777));
536		shar_quote(&shar->work, archive_entry_pathname(shar->entry), 1);
537		archive_strcat(&shar->work, "\n");
538
539		u = archive_entry_uname(shar->entry);
540		g = archive_entry_gname(shar->entry);
541		if (u != NULL || g != NULL) {
542			archive_strcat(&shar->work, "chown ");
543			if (u != NULL)
544				shar_quote(&shar->work, u, 1);
545			if (g != NULL) {
546				archive_strcat(&shar->work, ":");
547				shar_quote(&shar->work, g, 1);
548			}
549			archive_strcat(&shar->work, " ");
550			shar_quote(&shar->work,
551			    archive_entry_pathname(shar->entry), 1);
552			archive_strcat(&shar->work, "\n");
553		}
554
555		if ((p = archive_entry_fflags_text(shar->entry)) != NULL) {
556			archive_string_sprintf(&shar->work, "chflags %s ", p);
557			shar_quote(&shar->work,
558			    archive_entry_pathname(shar->entry), 1);
559			archive_strcat(&shar->work, "\n");
560		}
561
562		/* TODO: restore ACLs */
563
564	} else {
565		if (shar->has_data) {
566			/* Finish sed-encoded data:  ensure last line ends. */
567			if (!shar->end_of_line)
568				archive_strappend_char(&shar->work, '\n');
569			archive_strcat(&shar->work, "SHAR_END\n");
570		}
571	}
572
573	archive_entry_free(shar->entry);
574	shar->entry = NULL;
575
576	if (shar->work.length < 65536)
577		return (ARCHIVE_OK);
578
579	ret = __archive_write_output(a, shar->work.s, shar->work.length);
580	if (ret != ARCHIVE_OK)
581		return (ARCHIVE_FATAL);
582	archive_string_empty(&shar->work);
583
584	return (ARCHIVE_OK);
585}
586
587static int
588archive_write_shar_close(struct archive_write *a)
589{
590	struct shar *shar;
591	int ret;
592
593	/*
594	 * TODO: Accumulate list of directory names/modes and
595	 * fix them all up at end-of-archive.
596	 */
597
598	shar = (struct shar *)a->format_data;
599
600	/*
601	 * Only write the end-of-archive markers if the archive was
602	 * actually started.  This avoids problems if someone sets
603	 * shar format, then sets another format (which would invoke
604	 * shar_finish to free the format-specific data).
605	 */
606	if (shar->wrote_header == 0)
607		return (ARCHIVE_OK);
608
609	archive_strcat(&shar->work, "exit\n");
610
611	ret = __archive_write_output(a, shar->work.s, shar->work.length);
612	if (ret != ARCHIVE_OK)
613		return (ARCHIVE_FATAL);
614
615	/* Shar output is never padded. */
616	archive_write_set_bytes_in_last_block(&a->archive, 1);
617	/*
618	 * TODO: shar should also suppress padding of
619	 * uncompressed data within gzip/bzip2 streams.
620	 */
621
622	return (ARCHIVE_OK);
623}
624
625static int
626archive_write_shar_free(struct archive_write *a)
627{
628	struct shar *shar;
629
630	shar = (struct shar *)a->format_data;
631	if (shar == NULL)
632		return (ARCHIVE_OK);
633
634	archive_entry_free(shar->entry);
635	free(shar->last_dir);
636	archive_string_free(&(shar->work));
637	archive_string_free(&(shar->quoted_name));
638	free(shar);
639	a->format_data = NULL;
640	return (ARCHIVE_OK);
641}
642