1228753Smm/*-
2228753Smm * Copyright (c) 2003-2007 Tim Kientzle
3228753Smm * Copyright (c) 2006 Rudolf Marek SYSGO s.r.o.
4228753Smm * All rights reserved.
5228753Smm *
6228753Smm * Redistribution and use in source and binary forms, with or without
7228753Smm * modification, are permitted provided that the following conditions
8228753Smm * are met:
9228753Smm * 1. Redistributions of source code must retain the above copyright
10228753Smm *    notice, this list of conditions and the following disclaimer.
11228753Smm * 2. Redistributions in binary form must reproduce the above copyright
12228753Smm *    notice, this list of conditions and the following disclaimer in the
13228753Smm *    documentation and/or other materials provided with the distribution.
14228753Smm *
15228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18228753Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25228753Smm */
26228753Smm
27228753Smm#include "archive_platform.h"
28229592Smm__FBSDID("$FreeBSD$");
29228753Smm
30228753Smm#ifdef HAVE_ERRNO_H
31228753Smm#include <errno.h>
32228753Smm#endif
33228753Smm#include <stdio.h>
34228753Smm#ifdef HAVE_STDLIB_H
35228753Smm#include <stdlib.h>
36228753Smm#endif
37228753Smm#ifdef HAVE_STRING_H
38228753Smm#include <string.h>
39228753Smm#endif
40228753Smm
41228753Smm#include "archive.h"
42228753Smm#include "archive_entry.h"
43228753Smm#include "archive_private.h"
44228753Smm#include "archive_write_private.h"
45228753Smm
46228753Smmstatic ssize_t	archive_write_newc_data(struct archive_write *,
47228753Smm		    const void *buff, size_t s);
48228753Smmstatic int	archive_write_newc_finish(struct archive_write *);
49228753Smmstatic int	archive_write_newc_destroy(struct archive_write *);
50228753Smmstatic int	archive_write_newc_finish_entry(struct archive_write *);
51228753Smmstatic int	archive_write_newc_header(struct archive_write *,
52228753Smm		    struct archive_entry *);
53228753Smmstatic int	format_hex(int64_t, void *, int);
54228753Smmstatic int64_t	format_hex_recursive(int64_t, char *, int);
55228753Smm
56228753Smmstruct cpio {
57228753Smm	uint64_t	  entry_bytes_remaining;
58228753Smm	int		  padding;
59228753Smm};
60228753Smm
61228753Smmstruct cpio_header_newc {
62228753Smm	char	c_magic[6];
63228753Smm	char	c_ino[8];
64228753Smm	char	c_mode[8];
65228753Smm	char	c_uid[8];
66228753Smm	char	c_gid[8];
67228753Smm	char	c_nlink[8];
68228753Smm	char	c_mtime[8];
69228753Smm	char	c_filesize[8];
70228753Smm	char	c_devmajor[8];
71228753Smm	char	c_devminor[8];
72228753Smm	char	c_rdevmajor[8];
73228753Smm	char	c_rdevminor[8];
74228753Smm	char	c_namesize[8];
75228753Smm	char	c_checksum[8];
76228753Smm};
77228753Smm
78228753Smm/* Logic trick: difference between 'n' and next multiple of 4 */
79228753Smm#define PAD4(n)	(3 & (1 + ~(n)))
80228753Smm
81228753Smm/*
82228753Smm * Set output format to 'cpio' format.
83228753Smm */
84228753Smmint
85228753Smmarchive_write_set_format_cpio_newc(struct archive *_a)
86228753Smm{
87228753Smm	struct archive_write *a = (struct archive_write *)_a;
88228753Smm	struct cpio *cpio;
89228753Smm
90228753Smm	/* If someone else was already registered, unregister them. */
91228753Smm	if (a->format_destroy != NULL)
92228753Smm		(a->format_destroy)(a);
93228753Smm
94228753Smm	cpio = (struct cpio *)malloc(sizeof(*cpio));
95228753Smm	if (cpio == NULL) {
96228753Smm		archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data");
97228753Smm		return (ARCHIVE_FATAL);
98228753Smm	}
99228753Smm	memset(cpio, 0, sizeof(*cpio));
100228753Smm	a->format_data = cpio;
101228753Smm
102228753Smm	a->pad_uncompressed = 1;
103228753Smm	a->format_name = "cpio";
104228753Smm	a->format_write_header = archive_write_newc_header;
105228753Smm	a->format_write_data = archive_write_newc_data;
106228753Smm	a->format_finish_entry = archive_write_newc_finish_entry;
107228753Smm	a->format_finish = archive_write_newc_finish;
108228753Smm	a->format_destroy = archive_write_newc_destroy;
109228753Smm	a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC;
110228753Smm	a->archive.archive_format_name = "SVR4 cpio nocrc";
111228753Smm	return (ARCHIVE_OK);
112228753Smm}
113228753Smm
114228753Smmstatic int
115228753Smmarchive_write_newc_header(struct archive_write *a, struct archive_entry *entry)
116228753Smm{
117228753Smm	int64_t ino;
118228753Smm	struct cpio *cpio;
119228753Smm	const char *p, *path;
120228753Smm	int pathlength, ret, ret2;
121228753Smm	struct cpio_header_newc	 h;
122228753Smm	int pad;
123228753Smm
124228753Smm	cpio = (struct cpio *)a->format_data;
125228753Smm	ret2 = ARCHIVE_OK;
126228753Smm
127228753Smm	path = archive_entry_pathname(entry);
128228753Smm	pathlength = (int)strlen(path) + 1; /* Include trailing null. */
129228753Smm
130228753Smm	memset(&h, 0, sizeof(h));
131228753Smm	format_hex(0x070701, &h.c_magic, sizeof(h.c_magic));
132228753Smm	format_hex(archive_entry_devmajor(entry), &h.c_devmajor,
133228753Smm	    sizeof(h.c_devmajor));
134228753Smm	format_hex(archive_entry_devminor(entry), &h.c_devminor,
135228753Smm	    sizeof(h.c_devminor));
136228753Smm
137228753Smm	ino = archive_entry_ino64(entry);
138228753Smm	if (ino > 0xffffffff) {
139228753Smm		archive_set_error(&a->archive, ERANGE,
140228753Smm		    "large inode number truncated");
141228753Smm		ret2 = ARCHIVE_WARN;
142228753Smm	}
143228753Smm
144228753Smm	format_hex(ino & 0xffffffff, &h.c_ino, sizeof(h.c_ino));
145228753Smm	format_hex(archive_entry_mode(entry), &h.c_mode, sizeof(h.c_mode));
146228753Smm	format_hex(archive_entry_uid(entry), &h.c_uid, sizeof(h.c_uid));
147228753Smm	format_hex(archive_entry_gid(entry), &h.c_gid, sizeof(h.c_gid));
148228753Smm	format_hex(archive_entry_nlink(entry), &h.c_nlink, sizeof(h.c_nlink));
149228753Smm	if (archive_entry_filetype(entry) == AE_IFBLK
150228753Smm	    || archive_entry_filetype(entry) == AE_IFCHR) {
151228753Smm	    format_hex(archive_entry_rdevmajor(entry), &h.c_rdevmajor, sizeof(h.c_rdevmajor));
152228753Smm	    format_hex(archive_entry_rdevminor(entry), &h.c_rdevminor, sizeof(h.c_rdevminor));
153228753Smm	} else {
154228753Smm	    format_hex(0, &h.c_rdevmajor, sizeof(h.c_rdevmajor));
155228753Smm	    format_hex(0, &h.c_rdevminor, sizeof(h.c_rdevminor));
156228753Smm	}
157228753Smm	format_hex(archive_entry_mtime(entry), &h.c_mtime, sizeof(h.c_mtime));
158228753Smm	format_hex(pathlength, &h.c_namesize, sizeof(h.c_namesize));
159228753Smm	format_hex(0, &h.c_checksum, sizeof(h.c_checksum));
160228753Smm
161228753Smm	/* Non-regular files don't store bodies. */
162228753Smm	if (archive_entry_filetype(entry) != AE_IFREG)
163228753Smm		archive_entry_set_size(entry, 0);
164228753Smm
165228753Smm	/* Symlinks get the link written as the body of the entry. */
166228753Smm	p = archive_entry_symlink(entry);
167228753Smm	if (p != NULL  &&  *p != '\0')
168228753Smm		format_hex(strlen(p), &h.c_filesize, sizeof(h.c_filesize));
169228753Smm	else
170228753Smm		format_hex(archive_entry_size(entry),
171228753Smm		    &h.c_filesize, sizeof(h.c_filesize));
172228753Smm
173228753Smm	ret = (a->compressor.write)(a, &h, sizeof(h));
174228753Smm	if (ret != ARCHIVE_OK)
175228753Smm		return (ARCHIVE_FATAL);
176228753Smm
177228753Smm	/* Pad pathname to even length. */
178228753Smm	ret = (a->compressor.write)(a, path, pathlength);
179228753Smm	if (ret != ARCHIVE_OK)
180228753Smm		return (ARCHIVE_FATAL);
181228753Smm	pad = PAD4(pathlength + sizeof(struct cpio_header_newc));
182228753Smm	if (pad)
183228753Smm		ret = (a->compressor.write)(a, "\0\0\0", pad);
184228753Smm	if (ret != ARCHIVE_OK)
185228753Smm		return (ARCHIVE_FATAL);
186228753Smm
187228753Smm	cpio->entry_bytes_remaining = archive_entry_size(entry);
188228753Smm	cpio->padding = PAD4(cpio->entry_bytes_remaining);
189228753Smm
190228753Smm	/* Write the symlink now. */
191228753Smm	if (p != NULL  &&  *p != '\0') {
192228753Smm		ret = (a->compressor.write)(a, p, strlen(p));
193228753Smm		if (ret != ARCHIVE_OK)
194228753Smm			return (ARCHIVE_FATAL);
195228753Smm		pad = PAD4(strlen(p));
196228753Smm		ret = (a->compressor.write)(a, "\0\0\0", pad);
197228753Smm	}
198228753Smm
199228753Smm	if (ret == ARCHIVE_OK)
200228753Smm		ret = ret2;
201228753Smm	return (ret);
202228753Smm}
203228753Smm
204228753Smmstatic ssize_t
205228753Smmarchive_write_newc_data(struct archive_write *a, const void *buff, size_t s)
206228753Smm{
207228753Smm	struct cpio *cpio;
208228753Smm	int ret;
209228753Smm
210228753Smm	cpio = (struct cpio *)a->format_data;
211228753Smm	if (s > cpio->entry_bytes_remaining)
212228753Smm		s = cpio->entry_bytes_remaining;
213228753Smm
214228753Smm	ret = (a->compressor.write)(a, buff, s);
215228753Smm	cpio->entry_bytes_remaining -= s;
216228753Smm	if (ret >= 0)
217228753Smm		return (s);
218228753Smm	else
219228753Smm		return (ret);
220228753Smm}
221228753Smm
222228753Smm/*
223228753Smm * Format a number into the specified field.
224228753Smm */
225228753Smmstatic int
226228753Smmformat_hex(int64_t v, void *p, int digits)
227228753Smm{
228228753Smm	int64_t	max;
229228753Smm	int	ret;
230228753Smm
231228753Smm	max = (((int64_t)1) << (digits * 4)) - 1;
232228753Smm	if (v >= 0  &&  v <= max) {
233228753Smm	    format_hex_recursive(v, (char *)p, digits);
234228753Smm	    ret = 0;
235228753Smm	} else {
236228753Smm	    format_hex_recursive(max, (char *)p, digits);
237228753Smm	    ret = -1;
238228753Smm	}
239228753Smm	return (ret);
240228753Smm}
241228753Smm
242228753Smmstatic int64_t
243228753Smmformat_hex_recursive(int64_t v, char *p, int s)
244228753Smm{
245228753Smm	if (s == 0)
246228753Smm		return (v);
247228753Smm	v = format_hex_recursive(v, p+1, s-1);
248228753Smm	*p = "0123456789abcdef"[v & 0xf];
249228753Smm	return (v >> 4);
250228753Smm}
251228753Smm
252228753Smmstatic int
253228753Smmarchive_write_newc_finish(struct archive_write *a)
254228753Smm{
255228753Smm	int er;
256228753Smm	struct archive_entry *trailer;
257228753Smm
258228753Smm	trailer = archive_entry_new();
259228753Smm	archive_entry_set_nlink(trailer, 1);
260228753Smm	archive_entry_set_pathname(trailer, "TRAILER!!!");
261228753Smm	er = archive_write_newc_header(a, trailer);
262228753Smm	archive_entry_free(trailer);
263228753Smm	return (er);
264228753Smm}
265228753Smm
266228753Smmstatic int
267228753Smmarchive_write_newc_destroy(struct archive_write *a)
268228753Smm{
269228753Smm	struct cpio *cpio;
270228753Smm
271228753Smm	cpio = (struct cpio *)a->format_data;
272228753Smm	free(cpio);
273228753Smm	a->format_data = NULL;
274228753Smm	return (ARCHIVE_OK);
275228753Smm}
276228753Smm
277228753Smmstatic int
278228753Smmarchive_write_newc_finish_entry(struct archive_write *a)
279228753Smm{
280228753Smm	struct cpio *cpio;
281228753Smm	size_t to_write;
282228753Smm	int ret;
283228753Smm
284228753Smm	cpio = (struct cpio *)a->format_data;
285228753Smm	while (cpio->entry_bytes_remaining > 0) {
286228753Smm		to_write = cpio->entry_bytes_remaining < a->null_length ?
287228753Smm		    cpio->entry_bytes_remaining : a->null_length;
288228753Smm		ret = (a->compressor.write)(a, a->nulls, to_write);
289228753Smm		if (ret != ARCHIVE_OK)
290228753Smm			return (ret);
291228753Smm		cpio->entry_bytes_remaining -= to_write;
292228753Smm	}
293228753Smm	ret = (a->compressor.write)(a, a->nulls, cpio->padding);
294228753Smm	return (ret);
295228753Smm}
296