archive_write_set_format_shar.c revision 302408
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 299529 2016-05-12 10:16:16Z 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
46struct shar {
47	int			 dump;
48	int			 end_of_line;
49	struct archive_entry	*entry;
50	int			 has_data;
51	char			*last_dir;
52
53	/* Line buffer for uuencoded dump format */
54	char			 outbuff[45];
55	size_t			 outpos;
56
57	int			 wrote_header;
58	struct archive_string	 work;
59	struct archive_string	 quoted_name;
60};
61
62static int	archive_write_shar_close(struct archive_write *);
63static int	archive_write_shar_free(struct archive_write *);
64static int	archive_write_shar_header(struct archive_write *,
65		    struct archive_entry *);
66static ssize_t	archive_write_shar_data_sed(struct archive_write *,
67		    const void * buff, size_t);
68static ssize_t	archive_write_shar_data_uuencode(struct archive_write *,
69		    const void * buff, size_t);
70static int	archive_write_shar_finish_entry(struct archive_write *);
71
72/*
73 * Copy the given string to the buffer, quoting all shell meta characters
74 * found.
75 */
76static void
77shar_quote(struct archive_string *buf, const char *str, int in_shell)
78{
79	static const char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~";
80	size_t len;
81
82	while (*str != '\0') {
83		if ((len = strcspn(str, meta)) != 0) {
84			archive_strncat(buf, str, len);
85			str += len;
86		} else if (*str == '\n') {
87			if (in_shell)
88				archive_strcat(buf, "\"\n\"");
89			else
90				archive_strcat(buf, "\\n");
91			++str;
92		} else {
93			archive_strappend_char(buf, '\\');
94			archive_strappend_char(buf, *str);
95			++str;
96		}
97	}
98}
99
100/*
101 * Set output format to 'shar' format.
102 */
103int
104archive_write_set_format_shar(struct archive *_a)
105{
106	struct archive_write *a = (struct archive_write *)_a;
107	struct shar *shar;
108
109	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
110	    ARCHIVE_STATE_NEW, "archive_write_set_format_shar");
111
112	/* If someone else was already registered, unregister them. */
113	if (a->format_free != NULL)
114		(a->format_free)(a);
115
116	shar = (struct shar *)malloc(sizeof(*shar));
117	if (shar == NULL) {
118		archive_set_error(&a->archive, ENOMEM, "Can't allocate shar data");
119		return (ARCHIVE_FATAL);
120	}
121	memset(shar, 0, sizeof(*shar));
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	if (shar->entry)
174		archive_entry_free(shar->entry);
175	shar->entry = archive_entry_clone(entry);
176	name = archive_entry_pathname(entry);
177
178	/* Handle some preparatory issues. */
179	switch(archive_entry_filetype(entry)) {
180	case AE_IFREG:
181		/* Only regular files have non-zero size. */
182		break;
183	case AE_IFDIR:
184		archive_entry_set_size(entry, 0);
185		/* Don't bother trying to recreate '.' */
186		if (strcmp(name, ".") == 0  ||  strcmp(name, "./") == 0)
187			return (ARCHIVE_OK);
188		break;
189	case AE_IFIFO:
190	case AE_IFCHR:
191	case AE_IFBLK:
192		/* All other file types have zero size in the archive. */
193		archive_entry_set_size(entry, 0);
194		break;
195	default:
196		archive_entry_set_size(entry, 0);
197		if (archive_entry_hardlink(entry) == NULL &&
198		    archive_entry_symlink(entry) == NULL) {
199			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
200			    "shar format cannot archive this");
201			return (ARCHIVE_WARN);
202		}
203	}
204
205	archive_string_empty(&shar->quoted_name);
206	shar_quote(&shar->quoted_name, name, 1);
207
208	/* Stock preparation for all file types. */
209	archive_string_sprintf(&shar->work, "echo x %s\n", shar->quoted_name.s);
210
211	if (archive_entry_filetype(entry) != AE_IFDIR) {
212		/* Try to create the dir. */
213		p = strdup(name);
214		pp = strrchr(p, '/');
215		/* If there is a / character, try to create the dir. */
216		if (pp != NULL) {
217			*pp = '\0';
218
219			/* Try to avoid a lot of redundant mkdir commands. */
220			if (strcmp(p, ".") == 0) {
221				/* Don't try to "mkdir ." */
222				free(p);
223			} else if (shar->last_dir == NULL) {
224				archive_strcat(&shar->work, "mkdir -p ");
225				shar_quote(&shar->work, p, 1);
226				archive_strcat(&shar->work,
227				    " > /dev/null 2>&1\n");
228				shar->last_dir = p;
229			} else if (strcmp(p, shar->last_dir) == 0) {
230				/* We've already created this exact dir. */
231				free(p);
232			} else if (strlen(p) < strlen(shar->last_dir) &&
233			    strncmp(p, shar->last_dir, strlen(p)) == 0) {
234				/* We've already created a subdir. */
235				free(p);
236			} else {
237				archive_strcat(&shar->work, "mkdir -p ");
238				shar_quote(&shar->work, p, 1);
239				archive_strcat(&shar->work,
240				    " > /dev/null 2>&1\n");
241				shar->last_dir = p;
242			}
243		} else {
244			free(p);
245		}
246	}
247
248	/* Handle file-type specific issues. */
249	shar->has_data = 0;
250	if ((linkname = archive_entry_hardlink(entry)) != NULL) {
251		archive_strcat(&shar->work, "ln -f ");
252		shar_quote(&shar->work, linkname, 1);
253		archive_string_sprintf(&shar->work, " %s\n",
254		    shar->quoted_name.s);
255	} else if ((linkname = archive_entry_symlink(entry)) != NULL) {
256		archive_strcat(&shar->work, "ln -fs ");
257		shar_quote(&shar->work, linkname, 1);
258		archive_string_sprintf(&shar->work, " %s\n",
259		    shar->quoted_name.s);
260	} else {
261		switch(archive_entry_filetype(entry)) {
262		case AE_IFREG:
263			if (archive_entry_size(entry) == 0) {
264				/* More portable than "touch." */
265				archive_string_sprintf(&shar->work,
266				    "test -e \"%s\" || :> \"%s\"\n",
267				    shar->quoted_name.s, shar->quoted_name.s);
268			} else {
269				if (shar->dump) {
270					unsigned int mode = archive_entry_mode(entry) & 0777;
271					archive_string_sprintf(&shar->work,
272					    "uudecode -p > %s << 'SHAR_END'\n",
273					    shar->quoted_name.s);
274					archive_string_sprintf(&shar->work,
275					    "begin %o ", mode);
276					shar_quote(&shar->work, name, 0);
277					archive_strcat(&shar->work, "\n");
278				} else {
279					archive_string_sprintf(&shar->work,
280					    "sed 's/^X//' > %s << 'SHAR_END'\n",
281					    shar->quoted_name.s);
282				}
283				shar->has_data = 1;
284				shar->end_of_line = 1;
285				shar->outpos = 0;
286			}
287			break;
288		case AE_IFDIR:
289			archive_string_sprintf(&shar->work,
290			    "mkdir -p %s > /dev/null 2>&1\n",
291			    shar->quoted_name.s);
292			/* Record that we just created this directory. */
293			if (shar->last_dir != NULL)
294				free(shar->last_dir);
295
296			shar->last_dir = strdup(name);
297			/* Trim a trailing '/'. */
298			pp = strrchr(shar->last_dir, '/');
299			if (pp != NULL && pp[1] == '\0')
300				*pp = '\0';
301			/*
302			 * TODO: Put dir name/mode on a list to be fixed
303			 * up at end of archive.
304			 */
305			break;
306		case AE_IFIFO:
307			archive_string_sprintf(&shar->work,
308			    "mkfifo %s\n", shar->quoted_name.s);
309			break;
310		case AE_IFCHR:
311			archive_string_sprintf(&shar->work,
312			    "mknod %s c %ju %ju\n", shar->quoted_name.s,
313			    (uintmax_t)archive_entry_rdevmajor(entry),
314			    (uintmax_t)archive_entry_rdevminor(entry));
315			break;
316		case AE_IFBLK:
317			archive_string_sprintf(&shar->work,
318			    "mknod %s b %ju %ju\n", shar->quoted_name.s,
319			    (uintmax_t)archive_entry_rdevmajor(entry),
320			    (uintmax_t)archive_entry_rdevminor(entry));
321			break;
322		default:
323			return (ARCHIVE_WARN);
324		}
325	}
326
327	return (ARCHIVE_OK);
328}
329
330static ssize_t
331archive_write_shar_data_sed(struct archive_write *a, const void *buff, size_t n)
332{
333	static const size_t ensured = 65533;
334	struct shar *shar;
335	const char *src;
336	char *buf, *buf_end;
337	int ret;
338	size_t written = n;
339
340	shar = (struct shar *)a->format_data;
341	if (!shar->has_data || n == 0)
342		return (0);
343
344	src = (const char *)buff;
345
346	/*
347	 * ensure is the number of bytes in buffer before expanding the
348	 * current character.  Each operation writes the current character
349	 * and optionally the start-of-new-line marker.  This can happen
350	 * twice before entering the loop, so make sure three additional
351	 * bytes can be written.
352	 */
353	if (archive_string_ensure(&shar->work, ensured + 3) == NULL) {
354		archive_set_error(&a->archive, ENOMEM, "Out of memory");
355		return (ARCHIVE_FATAL);
356	}
357
358	if (shar->work.length > ensured) {
359		ret = __archive_write_output(a, shar->work.s,
360		    shar->work.length);
361		if (ret != ARCHIVE_OK)
362			return (ARCHIVE_FATAL);
363		archive_string_empty(&shar->work);
364	}
365	buf = shar->work.s + shar->work.length;
366	buf_end = shar->work.s + ensured;
367
368	if (shar->end_of_line) {
369		*buf++ = 'X';
370		shar->end_of_line = 0;
371	}
372
373	while (n-- != 0) {
374		if ((*buf++ = *src++) == '\n') {
375			if (n == 0)
376				shar->end_of_line = 1;
377			else
378				*buf++ = 'X';
379		}
380
381		if (buf >= buf_end) {
382			shar->work.length = buf - shar->work.s;
383			ret = __archive_write_output(a, shar->work.s,
384			    shar->work.length);
385			if (ret != ARCHIVE_OK)
386				return (ARCHIVE_FATAL);
387			archive_string_empty(&shar->work);
388			buf = shar->work.s;
389		}
390	}
391
392	shar->work.length = buf - shar->work.s;
393
394	return (written);
395}
396
397#define	UUENC(c)	(((c)!=0) ? ((c) & 077) + ' ': '`')
398
399static void
400uuencode_group(const char _in[3], char out[4])
401{
402	const unsigned char *in = (const unsigned char *)_in;
403	int t;
404
405	t = (in[0] << 16) | (in[1] << 8) | in[2];
406	out[0] = UUENC( 0x3f & (t >> 18) );
407	out[1] = UUENC( 0x3f & (t >> 12) );
408	out[2] = UUENC( 0x3f & (t >> 6) );
409	out[3] = UUENC( 0x3f & t );
410}
411
412static int
413_uuencode_line(struct archive_write *a, struct shar *shar, const char *inbuf, size_t len)
414{
415	char *buf;
416	size_t alloc_len;
417
418	/* len <= 45 -> expanded to 60 + len byte + new line */
419	alloc_len = shar->work.length + 62;
420	if (archive_string_ensure(&shar->work, alloc_len) == NULL) {
421		archive_set_error(&a->archive, ENOMEM, "Out of memory");
422		return (ARCHIVE_FATAL);
423	}
424
425	buf = shar->work.s + shar->work.length;
426	*buf++ = UUENC(len);
427	while (len >= 3) {
428		uuencode_group(inbuf, buf);
429		len -= 3;
430		inbuf += 3;
431		buf += 4;
432	}
433	if (len != 0) {
434		char tmp_buf[3];
435		tmp_buf[0] = inbuf[0];
436		if (len == 1)
437			tmp_buf[1] = '\0';
438		else
439			tmp_buf[1] = inbuf[1];
440		tmp_buf[2] = '\0';
441		uuencode_group(tmp_buf, buf);
442		buf += 4;
443	}
444	*buf++ = '\n';
445	if ((buf - shar->work.s) > (ptrdiff_t)(shar->work.length + 62)) {
446		archive_set_error(&a->archive,
447		    ARCHIVE_ERRNO_MISC, "Buffer overflow");
448		return (ARCHIVE_FATAL);
449	}
450	shar->work.length = buf - shar->work.s;
451	return (ARCHIVE_OK);
452}
453
454#define uuencode_line(__a, __shar, __inbuf, __len) \
455	do { \
456		int r = _uuencode_line(__a, __shar, __inbuf, __len); \
457		if (r != ARCHIVE_OK) \
458			return (ARCHIVE_FATAL); \
459	} while (0)
460
461static ssize_t
462archive_write_shar_data_uuencode(struct archive_write *a, const void *buff,
463    size_t length)
464{
465	struct shar *shar;
466	const char *src;
467	size_t n;
468	int ret;
469
470	shar = (struct shar *)a->format_data;
471	if (!shar->has_data)
472		return (ARCHIVE_OK);
473	src = (const char *)buff;
474
475	if (shar->outpos != 0) {
476		n = 45 - shar->outpos;
477		if (n > length)
478			n = length;
479		memcpy(shar->outbuff + shar->outpos, src, n);
480		if (shar->outpos + n < 45) {
481			shar->outpos += n;
482			return length;
483		}
484		uuencode_line(a, shar, shar->outbuff, 45);
485		src += n;
486		n = length - n;
487	} else {
488		n = length;
489	}
490
491	while (n >= 45) {
492		uuencode_line(a, shar, src, 45);
493		src += 45;
494		n -= 45;
495
496		if (shar->work.length < 65536)
497			continue;
498		ret = __archive_write_output(a, shar->work.s,
499		    shar->work.length);
500		if (ret != ARCHIVE_OK)
501			return (ARCHIVE_FATAL);
502		archive_string_empty(&shar->work);
503	}
504	if (n != 0) {
505		memcpy(shar->outbuff, src, n);
506		shar->outpos = n;
507	}
508	return (length);
509}
510
511static int
512archive_write_shar_finish_entry(struct archive_write *a)
513{
514	const char *g, *p, *u;
515	struct shar *shar;
516	int ret;
517
518	shar = (struct shar *)a->format_data;
519	if (shar->entry == NULL)
520		return (0);
521
522	if (shar->dump) {
523		/* Finish uuencoded data. */
524		if (shar->has_data) {
525			if (shar->outpos > 0)
526				uuencode_line(a, shar, shar->outbuff,
527				    shar->outpos);
528			archive_strcat(&shar->work, "`\nend\n");
529			archive_strcat(&shar->work, "SHAR_END\n");
530		}
531		/* Restore file mode, owner, flags. */
532		/*
533		 * TODO: Don't immediately restore mode for
534		 * directories; defer that to end of script.
535		 */
536		archive_string_sprintf(&shar->work, "chmod %o ",
537		    (unsigned int)(archive_entry_mode(shar->entry) & 07777));
538		shar_quote(&shar->work, archive_entry_pathname(shar->entry), 1);
539		archive_strcat(&shar->work, "\n");
540
541		u = archive_entry_uname(shar->entry);
542		g = archive_entry_gname(shar->entry);
543		if (u != NULL || g != NULL) {
544			archive_strcat(&shar->work, "chown ");
545			if (u != NULL)
546				shar_quote(&shar->work, u, 1);
547			if (g != NULL) {
548				archive_strcat(&shar->work, ":");
549				shar_quote(&shar->work, g, 1);
550			}
551			archive_strcat(&shar->work, " ");
552			shar_quote(&shar->work,
553			    archive_entry_pathname(shar->entry), 1);
554			archive_strcat(&shar->work, "\n");
555		}
556
557		if ((p = archive_entry_fflags_text(shar->entry)) != NULL) {
558			archive_string_sprintf(&shar->work, "chflags %s ", p);
559			shar_quote(&shar->work,
560			    archive_entry_pathname(shar->entry), 1);
561			archive_strcat(&shar->work, "\n");
562		}
563
564		/* TODO: restore ACLs */
565
566	} else {
567		if (shar->has_data) {
568			/* Finish sed-encoded data:  ensure last line ends. */
569			if (!shar->end_of_line)
570				archive_strappend_char(&shar->work, '\n');
571			archive_strcat(&shar->work, "SHAR_END\n");
572		}
573	}
574
575	archive_entry_free(shar->entry);
576	shar->entry = NULL;
577
578	if (shar->work.length < 65536)
579		return (ARCHIVE_OK);
580
581	ret = __archive_write_output(a, shar->work.s, shar->work.length);
582	if (ret != ARCHIVE_OK)
583		return (ARCHIVE_FATAL);
584	archive_string_empty(&shar->work);
585
586	return (ARCHIVE_OK);
587}
588
589static int
590archive_write_shar_close(struct archive_write *a)
591{
592	struct shar *shar;
593	int ret;
594
595	/*
596	 * TODO: Accumulate list of directory names/modes and
597	 * fix them all up at end-of-archive.
598	 */
599
600	shar = (struct shar *)a->format_data;
601
602	/*
603	 * Only write the end-of-archive markers if the archive was
604	 * actually started.  This avoids problems if someone sets
605	 * shar format, then sets another format (which would invoke
606	 * shar_finish to free the format-specific data).
607	 */
608	if (shar->wrote_header == 0)
609		return (ARCHIVE_OK);
610
611	archive_strcat(&shar->work, "exit\n");
612
613	ret = __archive_write_output(a, shar->work.s, shar->work.length);
614	if (ret != ARCHIVE_OK)
615		return (ARCHIVE_FATAL);
616
617	/* Shar output is never padded. */
618	archive_write_set_bytes_in_last_block(&a->archive, 1);
619	/*
620	 * TODO: shar should also suppress padding of
621	 * uncompressed data within gzip/bzip2 streams.
622	 */
623
624	return (ARCHIVE_OK);
625}
626
627static int
628archive_write_shar_free(struct archive_write *a)
629{
630	struct shar *shar;
631
632	shar = (struct shar *)a->format_data;
633	if (shar == NULL)
634		return (ARCHIVE_OK);
635
636	archive_entry_free(shar->entry);
637	free(shar->last_dir);
638	archive_string_free(&(shar->work));
639	archive_string_free(&(shar->quoted_name));
640	free(shar);
641	a->format_data = NULL;
642	return (ARCHIVE_OK);
643}
644