1caf54c4fSMartin Matuska /*-
2caf54c4fSMartin Matuska  * Copyright (c) 2003-2007 Tim Kientzle
3caf54c4fSMartin Matuska  * All rights reserved.
4caf54c4fSMartin Matuska  *
5caf54c4fSMartin Matuska  * Redistribution and use in source and binary forms, with or without
6caf54c4fSMartin Matuska  * modification, are permitted provided that the following conditions
7caf54c4fSMartin Matuska  * are met:
8caf54c4fSMartin Matuska  * 1. Redistributions of source code must retain the above copyright
9caf54c4fSMartin Matuska  *    notice, this list of conditions and the following disclaimer.
10caf54c4fSMartin Matuska  * 2. Redistributions in binary form must reproduce the above copyright
11caf54c4fSMartin Matuska  *    notice, this list of conditions and the following disclaimer in the
12caf54c4fSMartin Matuska  *    documentation and/or other materials provided with the distribution.
13caf54c4fSMartin Matuska  *
14caf54c4fSMartin Matuska  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15caf54c4fSMartin Matuska  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16caf54c4fSMartin Matuska  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17caf54c4fSMartin Matuska  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18caf54c4fSMartin Matuska  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19caf54c4fSMartin Matuska  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20caf54c4fSMartin Matuska  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21caf54c4fSMartin Matuska  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22caf54c4fSMartin Matuska  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23caf54c4fSMartin Matuska  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24caf54c4fSMartin Matuska  */
25caf54c4fSMartin Matuska 
26caf54c4fSMartin Matuska #include "archive_platform.h"
27caf54c4fSMartin Matuska 
28caf54c4fSMartin Matuska #ifdef HAVE_SYS_STAT_H
29caf54c4fSMartin Matuska #include <sys/stat.h>
30caf54c4fSMartin Matuska #endif
31caf54c4fSMartin Matuska #ifdef HAVE_ERRNO_H
32caf54c4fSMartin Matuska #include <errno.h>
33caf54c4fSMartin Matuska #endif
34caf54c4fSMartin Matuska #ifdef HAVE_FCNTL_H
35caf54c4fSMartin Matuska #include <fcntl.h>
36caf54c4fSMartin Matuska #endif
37caf54c4fSMartin Matuska #ifdef HAVE_STDLIB_H
38caf54c4fSMartin Matuska #include <stdlib.h>
39caf54c4fSMartin Matuska #endif
40caf54c4fSMartin Matuska #ifdef HAVE_STRING_H
41caf54c4fSMartin Matuska #include <string.h>
42caf54c4fSMartin Matuska #endif
43caf54c4fSMartin Matuska #ifdef HAVE_UNISTD_H
44caf54c4fSMartin Matuska #include <unistd.h>
45caf54c4fSMartin Matuska #endif
46caf54c4fSMartin Matuska 
47caf54c4fSMartin Matuska #include "archive.h"
48acc60b03SMartin Matuska #include "archive_private.h"
496c95142eSMartin Matuska #include "archive_string.h"
50caf54c4fSMartin Matuska 
51caf54c4fSMartin Matuska #ifndef O_BINARY
52caf54c4fSMartin Matuska #define O_BINARY 0
53caf54c4fSMartin Matuska #endif
54acc60b03SMartin Matuska #ifndef O_CLOEXEC
55acc60b03SMartin Matuska #define O_CLOEXEC	0
56acc60b03SMartin Matuska #endif
57caf54c4fSMartin Matuska 
58caf54c4fSMartin Matuska struct write_file_data {
59caf54c4fSMartin Matuska 	int		fd;
60fd082e96SMartin Matuska 	struct archive_mstring filename;
61caf54c4fSMartin Matuska };
62caf54c4fSMartin Matuska 
63caf54c4fSMartin Matuska static int	file_close(struct archive *, void *);
64c3afd20fSMartin Matuska static int	file_free(struct archive *, void *);
65caf54c4fSMartin Matuska static int	file_open(struct archive *, void *);
66caf54c4fSMartin Matuska static ssize_t	file_write(struct archive *, void *, const void *buff, size_t);
67fd082e96SMartin Matuska static int	open_filename(struct archive *, int, const void *);
68caf54c4fSMartin Matuska 
69caf54c4fSMartin Matuska int
archive_write_open_file(struct archive * a,const char * filename)70caf54c4fSMartin Matuska archive_write_open_file(struct archive *a, const char *filename)
71caf54c4fSMartin Matuska {
72caf54c4fSMartin Matuska 	return (archive_write_open_filename(a, filename));
73caf54c4fSMartin Matuska }
74caf54c4fSMartin Matuska 
75caf54c4fSMartin Matuska int
archive_write_open_filename(struct archive * a,const char * filename)76caf54c4fSMartin Matuska archive_write_open_filename(struct archive *a, const char *filename)
77caf54c4fSMartin Matuska {
78caf54c4fSMartin Matuska 
79caf54c4fSMartin Matuska 	if (filename == NULL || filename[0] == '\0')
80caf54c4fSMartin Matuska 		return (archive_write_open_fd(a, 1));
81caf54c4fSMartin Matuska 
82fd082e96SMartin Matuska 	return (open_filename(a, 1, filename));
83caf54c4fSMartin Matuska }
84caf54c4fSMartin Matuska 
856c95142eSMartin Matuska int
archive_write_open_filename_w(struct archive * a,const wchar_t * filename)866c95142eSMartin Matuska archive_write_open_filename_w(struct archive *a, const wchar_t *filename)
876c95142eSMartin Matuska {
886c95142eSMartin Matuska 
896c95142eSMartin Matuska 	if (filename == NULL || filename[0] == L'\0')
906c95142eSMartin Matuska 		return (archive_write_open_fd(a, 1));
916c95142eSMartin Matuska 
92fd082e96SMartin Matuska 	return (open_filename(a, 0, filename));
93fd082e96SMartin Matuska }
94fd082e96SMartin Matuska 
95fd082e96SMartin Matuska static int
open_filename(struct archive * a,int mbs_fn,const void * filename)96fd082e96SMartin Matuska open_filename(struct archive *a, int mbs_fn, const void *filename)
97fd082e96SMartin Matuska {
98fd082e96SMartin Matuska 	struct write_file_data *mine;
99fd082e96SMartin Matuska 	int r;
100fd082e96SMartin Matuska 
101fd082e96SMartin Matuska 	mine = (struct write_file_data *)calloc(1, sizeof(*mine));
1026c95142eSMartin Matuska 	if (mine == NULL) {
1036c95142eSMartin Matuska 		archive_set_error(a, ENOMEM, "No memory");
1046c95142eSMartin Matuska 		return (ARCHIVE_FATAL);
1056c95142eSMartin Matuska 	}
106fd082e96SMartin Matuska 	if (mbs_fn)
107fd082e96SMartin Matuska 		r = archive_mstring_copy_mbs(&mine->filename, filename);
108fd082e96SMartin Matuska 	else
109fd082e96SMartin Matuska 		r = archive_mstring_copy_wcs(&mine->filename, filename);
110fd082e96SMartin Matuska 	if (r < 0) {
111fd082e96SMartin Matuska 		if (errno == ENOMEM) {
112fd082e96SMartin Matuska 			archive_set_error(a, ENOMEM, "No memory");
113fd082e96SMartin Matuska 			return (ARCHIVE_FATAL);
114fd082e96SMartin Matuska 		}
115fd082e96SMartin Matuska 		if (mbs_fn)
116fd082e96SMartin Matuska 			archive_set_error(a, ARCHIVE_ERRNO_MISC,
117fd082e96SMartin Matuska 			    "Can't convert '%s' to WCS",
118fd082e96SMartin Matuska 			    (const char *)filename);
119fd082e96SMartin Matuska 		else
120fd082e96SMartin Matuska 			archive_set_error(a, ARCHIVE_ERRNO_MISC,
121fd082e96SMartin Matuska 			    "Can't convert '%S' to MBS",
122fd082e96SMartin Matuska 			    (const wchar_t *)filename);
123fd082e96SMartin Matuska 		return (ARCHIVE_FAILED);
124fd082e96SMartin Matuska 	}
1256c95142eSMartin Matuska 	mine->fd = -1;
126c3afd20fSMartin Matuska 	return (archive_write_open2(a, mine,
127c3afd20fSMartin Matuska 		    file_open, file_write, file_close, file_free));
1286c95142eSMartin Matuska }
1296c95142eSMartin Matuska 
130caf54c4fSMartin Matuska static int
file_open(struct archive * a,void * client_data)131caf54c4fSMartin Matuska file_open(struct archive *a, void *client_data)
132caf54c4fSMartin Matuska {
133caf54c4fSMartin Matuska 	int flags;
134caf54c4fSMartin Matuska 	struct write_file_data *mine;
135caf54c4fSMartin Matuska 	struct stat st;
136fd082e96SMartin Matuska #if defined(_WIN32) && !defined(__CYGWIN__)
137fd082e96SMartin Matuska 	wchar_t *fullpath;
138fd082e96SMartin Matuska #endif
139fd082e96SMartin Matuska 	const wchar_t *wcs;
140fd082e96SMartin Matuska 	const char *mbs;
141caf54c4fSMartin Matuska 
142caf54c4fSMartin Matuska 	mine = (struct write_file_data *)client_data;
143acc60b03SMartin Matuska 	flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_CLOEXEC;
144caf54c4fSMartin Matuska 
145caf54c4fSMartin Matuska 	/*
146caf54c4fSMartin Matuska 	 * Open the file.
147caf54c4fSMartin Matuska 	 */
148fd082e96SMartin Matuska 	mbs = NULL; wcs = NULL;
1496c95142eSMartin Matuska #if defined(_WIN32) && !defined(__CYGWIN__)
150fd082e96SMartin Matuska 	if (archive_mstring_get_wcs(a, &mine->filename, &wcs) != 0) {
151fd082e96SMartin Matuska 		if (errno == ENOMEM)
152fd082e96SMartin Matuska 			archive_set_error(a, errno, "No memory");
153fd082e96SMartin Matuska 		else {
154fd082e96SMartin Matuska 			archive_mstring_get_mbs(a, &mine->filename, &mbs);
155fd082e96SMartin Matuska 			archive_set_error(a, errno,
156fd082e96SMartin Matuska 			    "Can't convert '%s' to WCS", mbs);
157fd082e96SMartin Matuska 		}
158fd082e96SMartin Matuska 		return (ARCHIVE_FATAL);
159fd082e96SMartin Matuska 	}
160fd082e96SMartin Matuska 	fullpath = __la_win_permissive_name_w(wcs);
1616c95142eSMartin Matuska 	if (fullpath != NULL) {
1626c95142eSMartin Matuska 		mine->fd = _wopen(fullpath, flags, 0666);
1636c95142eSMartin Matuska 		free(fullpath);
164fd082e96SMartin Matuska 	} else
165fd082e96SMartin Matuska 		mine->fd = _wopen(wcs, flags, 0666);
166fd082e96SMartin Matuska #else
167fd082e96SMartin Matuska 	if (archive_mstring_get_mbs(a, &mine->filename, &mbs) != 0) {
168fd082e96SMartin Matuska 		if (errno == ENOMEM)
169fd082e96SMartin Matuska 			archive_set_error(a, errno, "No memory");
170fd082e96SMartin Matuska 		else {
171fd082e96SMartin Matuska 			archive_mstring_get_wcs(a, &mine->filename, &wcs);
172fd082e96SMartin Matuska 			archive_set_error(a, errno,
173fd082e96SMartin Matuska 			    "Can't convert '%S' to MBS", wcs);
1746c95142eSMartin Matuska 		}
175fd082e96SMartin Matuska 		return (ARCHIVE_FATAL);
1766c95142eSMartin Matuska 	}
177fd082e96SMartin Matuska 	mine->fd = open(mbs, flags, 0666);
178acc60b03SMartin Matuska 	__archive_ensure_cloexec_flag(mine->fd);
179fd082e96SMartin Matuska #endif
1806c95142eSMartin Matuska 	if (mine->fd < 0) {
181fd082e96SMartin Matuska 		if (mbs != NULL)
182fd082e96SMartin Matuska 			archive_set_error(a, errno, "Failed to open '%s'", mbs);
183fd082e96SMartin Matuska 		else
184fd082e96SMartin Matuska 			archive_set_error(a, errno, "Failed to open '%S'", wcs);
1856c95142eSMartin Matuska 		return (ARCHIVE_FATAL);
1866c95142eSMartin Matuska 	}
1876c95142eSMartin Matuska 
1886c95142eSMartin Matuska 	if (fstat(mine->fd, &st) != 0) {
189fd082e96SMartin Matuska 		if (mbs != NULL)
190fd082e96SMartin Matuska 			archive_set_error(a, errno, "Couldn't stat '%s'", mbs);
191fd082e96SMartin Matuska 		else
192fd082e96SMartin Matuska 			archive_set_error(a, errno, "Couldn't stat '%S'", wcs);
1936c95142eSMartin Matuska 		return (ARCHIVE_FATAL);
1946c95142eSMartin Matuska 	}
195caf54c4fSMartin Matuska 
196caf54c4fSMartin Matuska 	/*
197caf54c4fSMartin Matuska 	 * Set up default last block handling.
198caf54c4fSMartin Matuska 	 */
199caf54c4fSMartin Matuska 	if (archive_write_get_bytes_in_last_block(a) < 0) {
200caf54c4fSMartin Matuska 		if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) ||
201caf54c4fSMartin Matuska 		    S_ISFIFO(st.st_mode))
202caf54c4fSMartin Matuska 			/* Pad last block when writing to device or FIFO. */
203caf54c4fSMartin Matuska 			archive_write_set_bytes_in_last_block(a, 0);
204caf54c4fSMartin Matuska 		else
205caf54c4fSMartin Matuska 			/* Don't pad last block otherwise. */
206caf54c4fSMartin Matuska 			archive_write_set_bytes_in_last_block(a, 1);
207caf54c4fSMartin Matuska 	}
208caf54c4fSMartin Matuska 
209caf54c4fSMartin Matuska 	/*
210caf54c4fSMartin Matuska 	 * If the output file is a regular file, don't add it to
211caf54c4fSMartin Matuska 	 * itself.  If it's a device file, it's okay to add the device
212caf54c4fSMartin Matuska 	 * entry to the output archive.
213caf54c4fSMartin Matuska 	 */
214caf54c4fSMartin Matuska 	if (S_ISREG(st.st_mode))
215caf54c4fSMartin Matuska 		archive_write_set_skip_file(a, st.st_dev, st.st_ino);
216caf54c4fSMartin Matuska 
217caf54c4fSMartin Matuska 	return (ARCHIVE_OK);
218caf54c4fSMartin Matuska }
219caf54c4fSMartin Matuska 
220caf54c4fSMartin Matuska static ssize_t
file_write(struct archive * a,void * client_data,const void * buff,size_t length)221fd082e96SMartin Matuska file_write(struct archive *a, void *client_data, const void *buff,
222fd082e96SMartin Matuska     size_t length)
223caf54c4fSMartin Matuska {
224caf54c4fSMartin Matuska 	struct write_file_data	*mine;
225caf54c4fSMartin Matuska 	ssize_t	bytesWritten;
226caf54c4fSMartin Matuska 
227caf54c4fSMartin Matuska 	mine = (struct write_file_data *)client_data;
228caf54c4fSMartin Matuska 	for (;;) {
229caf54c4fSMartin Matuska 		bytesWritten = write(mine->fd, buff, length);
230caf54c4fSMartin Matuska 		if (bytesWritten <= 0) {
231caf54c4fSMartin Matuska 			if (errno == EINTR)
232caf54c4fSMartin Matuska 				continue;
233caf54c4fSMartin Matuska 			archive_set_error(a, errno, "Write error");
234caf54c4fSMartin Matuska 			return (-1);
235caf54c4fSMartin Matuska 		}
236caf54c4fSMartin Matuska 		return (bytesWritten);
237caf54c4fSMartin Matuska 	}
238caf54c4fSMartin Matuska }
239caf54c4fSMartin Matuska 
240caf54c4fSMartin Matuska static int
file_close(struct archive * a,void * client_data)241caf54c4fSMartin Matuska file_close(struct archive *a, void *client_data)
242caf54c4fSMartin Matuska {
243caf54c4fSMartin Matuska 	struct write_file_data	*mine = (struct write_file_data *)client_data;
244caf54c4fSMartin Matuska 
245caf54c4fSMartin Matuska 	(void)a; /* UNUSED */
246cdf63a70SMartin Matuska 
247c3afd20fSMartin Matuska 	if (mine == NULL)
248c3afd20fSMartin Matuska 		return (ARCHIVE_FATAL);
249c3afd20fSMartin Matuska 
250cdf63a70SMartin Matuska 	if (mine->fd >= 0)
251caf54c4fSMartin Matuska 		close(mine->fd);
252cdf63a70SMartin Matuska 
253c3afd20fSMartin Matuska 	return (ARCHIVE_OK);
254c3afd20fSMartin Matuska }
255c3afd20fSMartin Matuska 
256c3afd20fSMartin Matuska static int
file_free(struct archive * a,void * client_data)257c3afd20fSMartin Matuska file_free(struct archive *a, void *client_data)
258c3afd20fSMartin Matuska {
259c3afd20fSMartin Matuska 	struct write_file_data	*mine = (struct write_file_data *)client_data;
260c3afd20fSMartin Matuska 
261c3afd20fSMartin Matuska 	(void)a; /* UNUSED */
262c3afd20fSMartin Matuska 
263c3afd20fSMartin Matuska 	if (mine == NULL)
264c3afd20fSMartin Matuska 		return (ARCHIVE_OK);
265c3afd20fSMartin Matuska 
266fd082e96SMartin Matuska 	archive_mstring_clean(&mine->filename);
267caf54c4fSMartin Matuska 	free(mine);
268caf54c4fSMartin Matuska 	return (ARCHIVE_OK);
269caf54c4fSMartin Matuska }
270