1 /*
2 * Copyright 2016-2017 Frank Hunleth
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "archive_open.h"
18 #include "progress.h"
19 #include "util.h"
20
21 #include <archive.h>
22 #include <errno.h>
23 #include <stdlib.h>
24 #include <limits.h>
25 #include <string.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29
30 #define DEFAULT_LIBARCHIVE_BLOCK_SIZE 16384
31
32 struct fwup_archive_data {
33 size_t current_frame_remaining;
34 bool is_stdin;
35 bool is_eof;
36 int fd;
37 struct fwup_progress *progress;
38
39 char name[PATH_MAX];
40 char buffer[DEFAULT_LIBARCHIVE_BLOCK_SIZE];
41 };
42
normal_read(struct archive * a,void * client_data,const void ** buff)43 static ssize_t normal_read(struct archive *a, void *client_data, const void **buff)
44 {
45 struct fwup_archive_data *ad = (struct fwup_archive_data *) client_data;
46
47 *buff = ad->buffer;
48 for (;;) {
49 ssize_t bytes_read = read(ad->fd, ad->buffer, sizeof(ad->buffer));
50 if (bytes_read < 0) {
51 if (errno == EINTR)
52 continue;
53
54 if (ad->is_stdin)
55 archive_set_error(a, errno, "Error reading stdin");
56 else
57 archive_set_error(a, errno, "Error reading '%s'", ad->name);
58
59 return -1;
60 }
61
62 if (ad->progress)
63 ad->progress->input_bytes += bytes_read;
64
65 return bytes_read;
66 }
67 }
68
normal_close(struct archive * a,void * client_data)69 static int normal_close(struct archive *a, void *client_data)
70 {
71 struct fwup_archive_data *ad = (struct fwup_archive_data *) client_data;
72 (void)a; /* UNUSED */
73
74 // NOTE: One very important difference between libarchive's default file
75 // reading implementation and this one is that libarchive drains stdin
76 // before closing it. This is the friendly thing to do when working with
77 // pipes since if data isn't needed at the end, you still have to consume
78 // it or the source of the pipe gets an error. This is NOT the behavior
79 // we want. We want to leave it up to other code for whether to drain or
80 // not. That way if there's an error, that code can choose to exit immediately
81 // and not pay the cost of transferring all of those bytes through the pipe.
82 // The pipe data may originate from a remote source and stopping the
83 // transmission immediately saves time.
84
85 // Only close files - not stdin.
86 if (ad->fd > 0)
87 close(ad->fd);
88
89 free(ad);
90 return ARCHIVE_OK;
91 }
92
framed_stdin_read(struct archive * a,void * client_data,const void ** buff)93 static ssize_t framed_stdin_read(struct archive *a, void *client_data, const void **buff)
94 {
95 struct fwup_archive_data *ad = (struct fwup_archive_data *) client_data;
96 if (ad->is_eof)
97 return 0;
98
99 *buff = ad->buffer;
100
101 size_t amount_read;
102
103 if (ad->current_frame_remaining == 0) {
104 uint32_t be_len;
105 amount_read = fread(&be_len, 1, sizeof(be_len), stdin);
106 if (amount_read != sizeof(be_len)) {
107 archive_set_error(a, errno, "Error reading stdin");
108 return -1;
109 }
110
111 if (be_len == 0) {
112 ad->is_eof = true;
113 return 0;
114 }
115 ad->current_frame_remaining = FROM_BIGENDIAN32(be_len);
116 }
117
118 size_t amount_to_read = ad->current_frame_remaining;
119 if (amount_to_read > sizeof(ad->buffer))
120 amount_to_read = sizeof(ad->buffer);
121
122 amount_read = fread(ad->buffer, 1, amount_to_read, stdin);
123 if (amount_read == 0) {
124 archive_set_error(a, EIO, "Received EOF even though framing indicated more bytes");
125 return -1;
126 } else {
127 ad->current_frame_remaining -= amount_read;
128 if (ad->progress)
129 ad->progress->input_bytes += amount_read;
130
131 return amount_read;
132 }
133 }
134
135 /**
136 * @brief Open the specified file for use with libarchive.
137 *
138 * This call sets up all of the libarchive callbacks properly for fwup.
139 *
140 * @param a a libarchive handle
141 * @param filename the file to open or NULL for stdin
142 * @param progress input progress is reported if non-NULL
143 * @return a libarchive error code (e.g., ARCHIVE_OK or ARCHIVE_FATAL)
144 */
fwup_archive_open_filename(struct archive * a,const char * filename,struct fwup_progress * progress)145 int fwup_archive_open_filename(struct archive *a, const char *filename, struct fwup_progress *progress)
146 {
147 struct fwup_archive_data *ad = (struct fwup_archive_data *) calloc(1, sizeof(struct fwup_archive_data));
148 if (ad == NULL) {
149 archive_set_error(a, ENOMEM, "No memory");
150 return ARCHIVE_FATAL;
151 }
152
153 ad->current_frame_remaining = 0;
154 ad->is_eof = false;
155 ad->is_stdin = (filename == NULL || filename[0] == '\0');
156 if (!ad->is_stdin)
157 strncpy(ad->name, filename, sizeof(ad->name) - 1);
158 ad->progress = progress;
159
160 if (ad->is_stdin) {
161 ad->fd = STDIN_FILENO;
162 #ifdef _WIN32
163 setmode(STDIN_FILENO, O_BINARY);
164 #endif
165 } else {
166 ad->fd = open(ad->name, O_RDONLY | O_WIN32_BINARY);
167 if (ad->fd < 0) {
168 archive_set_error(a, errno, "Failed to open '%s'", ad->name);
169 free(ad);
170 return ARCHIVE_FATAL;
171 }
172 #ifdef HAVE_FCNTL
173 (void) fcntl(ad->fd, F_SETFD, FD_CLOEXEC);
174 #endif
175 }
176
177 archive_read_set_callback_data(a, ad);
178 archive_read_set_close_callback(a, normal_close);
179
180 if (fwup_framing && ad->is_stdin) {
181 // If reading from standard in and framing is enabled, then
182 // it needs to be applied to the input too.
183 archive_read_set_read_callback(a, framed_stdin_read);
184 } else {
185 // Files and stdin w/o framing are handled similarly.
186 archive_read_set_read_callback(a, normal_read);
187 }
188
189 return archive_read_open1(a);
190 }
191
fwup_archive_read_data_block(struct archive * a,const void ** buff,size_t * s,int64_t * o)192 int fwup_archive_read_data_block(struct archive *a, const void **buff, size_t *s, int64_t *o)
193 {
194 // Handle case where archive_read_data_block returns a 0 byte read
195 // even though it's not at end of file. A second read gets past this.
196
197 int rc;
198 do {
199 rc = archive_read_data_block(a, buff, s, o);
200 } while (rc == ARCHIVE_OK && *s == 0);
201 return rc;
202 }