Import libarchive-3.0.4.
[dragonfly.git] / contrib / libarchive / libarchive / archive_write_open_filename.c
1 /*-
2  * Copyright (c) 2003-2007 Tim Kientzle
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 "archive_platform.h"
27 __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_open_filename.c 191165 2009-04-17 00:39:35Z kientzle $");
28
29 #ifdef HAVE_SYS_STAT_H
30 #include <sys/stat.h>
31 #endif
32 #ifdef HAVE_ERRNO_H
33 #include <errno.h>
34 #endif
35 #ifdef HAVE_FCNTL_H
36 #include <fcntl.h>
37 #endif
38 #ifdef HAVE_STDLIB_H
39 #include <stdlib.h>
40 #endif
41 #ifdef HAVE_STRING_H
42 #include <string.h>
43 #endif
44 #ifdef HAVE_UNISTD_H
45 #include <unistd.h>
46 #endif
47
48 #include "archive.h"
49 #include "archive_string.h"
50
51 #ifndef O_BINARY
52 #define O_BINARY 0
53 #endif
54
55 struct write_file_data {
56         int             fd;
57         struct archive_mstring filename;
58 };
59
60 static int      file_close(struct archive *, void *);
61 static int      file_open(struct archive *, void *);
62 static ssize_t  file_write(struct archive *, void *, const void *buff, size_t);
63 static int      open_filename(struct archive *, int, const void *);
64
65 int
66 archive_write_open_file(struct archive *a, const char *filename)
67 {
68         return (archive_write_open_filename(a, filename));
69 }
70
71 int
72 archive_write_open_filename(struct archive *a, const char *filename)
73 {
74
75         if (filename == NULL || filename[0] == '\0')
76                 return (archive_write_open_fd(a, 1));
77
78         return (open_filename(a, 1, filename));
79 }
80
81 int
82 archive_write_open_filename_w(struct archive *a, const wchar_t *filename)
83 {
84
85         if (filename == NULL || filename[0] == L'\0')
86                 return (archive_write_open_fd(a, 1));
87
88         return (open_filename(a, 0, filename));
89 }
90
91 static int
92 open_filename(struct archive *a, int mbs_fn, const void *filename)
93 {
94         struct write_file_data *mine;
95         int r;
96
97         mine = (struct write_file_data *)calloc(1, sizeof(*mine));
98         if (mine == NULL) {
99                 archive_set_error(a, ENOMEM, "No memory");
100                 return (ARCHIVE_FATAL);
101         }
102         if (mbs_fn)
103                 r = archive_mstring_copy_mbs(&mine->filename, filename);
104         else
105                 r = archive_mstring_copy_wcs(&mine->filename, filename);
106         if (r < 0) {
107                 if (errno == ENOMEM) {
108                         archive_set_error(a, ENOMEM, "No memory");
109                         return (ARCHIVE_FATAL);
110                 }
111                 if (mbs_fn)
112                         archive_set_error(a, ARCHIVE_ERRNO_MISC,
113                             "Can't convert '%s' to WCS",
114                             (const char *)filename);
115                 else
116                         archive_set_error(a, ARCHIVE_ERRNO_MISC,
117                             "Can't convert '%S' to MBS",
118                             (const wchar_t *)filename);
119                 return (ARCHIVE_FAILED);
120         }
121         mine->fd = -1;
122         return (archive_write_open(a, mine,
123                 file_open, file_write, file_close));
124 }
125
126 static int
127 file_open(struct archive *a, void *client_data)
128 {
129         int flags;
130         struct write_file_data *mine;
131         struct stat st;
132 #if defined(_WIN32) && !defined(__CYGWIN__)
133         wchar_t *fullpath;
134 #endif
135         const wchar_t *wcs;
136         const char *mbs;
137
138         mine = (struct write_file_data *)client_data;
139         flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
140
141         /*
142          * Open the file.
143          */
144         mbs = NULL; wcs = NULL;
145 #if defined(_WIN32) && !defined(__CYGWIN__)
146         if (archive_mstring_get_wcs(a, &mine->filename, &wcs) != 0) {
147                 if (errno == ENOMEM)
148                         archive_set_error(a, errno, "No memory");
149                 else {
150                         archive_mstring_get_mbs(a, &mine->filename, &mbs);
151                         archive_set_error(a, errno,
152                             "Can't convert '%s' to WCS", mbs);
153                 }
154                 return (ARCHIVE_FATAL);
155         }
156         fullpath = __la_win_permissive_name_w(wcs);
157         if (fullpath != NULL) {
158                 mine->fd = _wopen(fullpath, flags, 0666);
159                 free(fullpath);
160         } else
161                 mine->fd = _wopen(wcs, flags, 0666);
162 #else
163         if (archive_mstring_get_mbs(a, &mine->filename, &mbs) != 0) {
164                 if (errno == ENOMEM)
165                         archive_set_error(a, errno, "No memory");
166                 else {
167                         archive_mstring_get_wcs(a, &mine->filename, &wcs);
168                         archive_set_error(a, errno,
169                             "Can't convert '%S' to MBS", wcs);
170                 }
171                 return (ARCHIVE_FATAL);
172         }
173         mine->fd = open(mbs, flags, 0666);
174 #endif
175         if (mine->fd < 0) {
176                 if (mbs != NULL)
177                         archive_set_error(a, errno, "Failed to open '%s'", mbs);
178                 else
179                         archive_set_error(a, errno, "Failed to open '%S'", wcs);
180                 return (ARCHIVE_FATAL);
181         }
182
183         if (fstat(mine->fd, &st) != 0) {
184                 if (mbs != NULL)
185                         archive_set_error(a, errno, "Couldn't stat '%s'", mbs);
186                 else
187                         archive_set_error(a, errno, "Couldn't stat '%S'", wcs);
188                 return (ARCHIVE_FATAL);
189         }
190
191         /*
192          * Set up default last block handling.
193          */
194         if (archive_write_get_bytes_in_last_block(a) < 0) {
195                 if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) ||
196                     S_ISFIFO(st.st_mode))
197                         /* Pad last block when writing to device or FIFO. */
198                         archive_write_set_bytes_in_last_block(a, 0);
199                 else
200                         /* Don't pad last block otherwise. */
201                         archive_write_set_bytes_in_last_block(a, 1);
202         }
203
204         /*
205          * If the output file is a regular file, don't add it to
206          * itself.  If it's a device file, it's okay to add the device
207          * entry to the output archive.
208          */
209         if (S_ISREG(st.st_mode))
210                 archive_write_set_skip_file(a, st.st_dev, st.st_ino);
211
212         return (ARCHIVE_OK);
213 }
214
215 static ssize_t
216 file_write(struct archive *a, void *client_data, const void *buff,
217     size_t length)
218 {
219         struct write_file_data  *mine;
220         ssize_t bytesWritten;
221
222         mine = (struct write_file_data *)client_data;
223         for (;;) {
224                 bytesWritten = write(mine->fd, buff, length);
225                 if (bytesWritten <= 0) {
226                         if (errno == EINTR)
227                                 continue;
228                         archive_set_error(a, errno, "Write error");
229                         return (-1);
230                 }
231                 return (bytesWritten);
232         }
233 }
234
235 static int
236 file_close(struct archive *a, void *client_data)
237 {
238         struct write_file_data  *mine = (struct write_file_data *)client_data;
239
240         (void)a; /* UNUSED */
241         close(mine->fd);
242         archive_mstring_clean(&mine->filename);
243         free(mine);
244         return (ARCHIVE_OK);
245 }