]> git.ozlabs.org Git - petitboot/blob - lib/file/file.c
Support HTTP(S) proxies when downloading resources
[petitboot] / lib / file / file.c
1 /*
2  *  Copyright (C) 2013 Jeremy Kerr <jk@ozlabs.org>
3  *  Copyright (C) 2016 Raptor Engineering, LLC
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; version 2 of the License.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19 #include <fcntl.h>
20 #include <unistd.h>
21 #include <stdlib.h>
22 #include <errno.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25
26 #include <talloc/talloc.h>
27 #include <log/log.h>
28
29 #include "file.h"
30
31 #define MAX_FILENAME_SIZE       8192
32 #define FILE_XFER_BUFFER_SIZE   8192
33
34 static const int max_file_size = 1024 * 1024;
35
36 int copy_file_secure_dest(void *ctx, const char *source_file,
37                 char **destination_file)
38 {
39         char readlink_buffer[MAX_FILENAME_SIZE + 1];
40         char dest_filename[MAX_FILENAME_SIZE] = "";
41         char template[] = "/tmp/petitbootXXXXXX";
42         FILE *destination_handle, *source_handle;
43         int destination_fd, result = 0;
44         unsigned char *buffer;
45         ssize_t r;
46         size_t l1;
47         mode_t oldmask;
48
49         source_handle = fopen(source_file, "r");
50         if (!source_handle) {
51                 pb_log("%s: unable to open source file '%s': %m\n",
52                         __func__, source_file);
53                         return -1;
54         }
55
56         oldmask = umask(0644);
57         destination_fd = mkstemp(template);
58         umask(oldmask);
59         if (destination_fd < 0) {
60                 pb_log("%s: unable to create temp file, %m\n", __func__);
61                 fclose(source_handle);
62                 return -1;
63         }
64         destination_handle = fdopen(destination_fd, "w");
65         if (!destination_handle) {
66                 pb_log("%s: unable to open destination file, %m\n", __func__);
67                 fclose(source_handle);
68                 close(destination_fd);
69                 return -1;
70         }
71
72         buffer = talloc_array(ctx, unsigned char, FILE_XFER_BUFFER_SIZE);
73         if (!buffer) {
74                 pb_log("%s: failed: unable to allocate file transfer buffer\n",
75                         __func__);
76                 result = -1;
77                 goto out;
78         }
79
80         /* Copy data */
81         while ((l1 = fread(buffer, 1, FILE_XFER_BUFFER_SIZE, source_handle)) > 0) {
82                 size_t l2 = fwrite(buffer, 1, l1, destination_handle);
83                 if (l2 < l1) {
84                         if (ferror(destination_handle)) {
85                                 /* General error */
86                                 result = -1;
87                                 pb_log("%s: failed: unknown fault\n", __func__);
88                         }
89                         else {
90                                 /* No space on destination device */
91                                 result = -1;
92                                 pb_log("%s: failed: temporary storage full\n",
93                                         __func__);
94                         }
95                         break;
96                 }
97         }
98
99         if (result) {
100                 *destination_file = NULL;
101                 goto out;
102         }
103
104         snprintf(readlink_buffer, MAX_FILENAME_SIZE, "/proc/self/fd/%d",
105                 destination_fd);
106         r = readlink(readlink_buffer, dest_filename, MAX_FILENAME_SIZE);
107         if (r < 0) {
108                 /* readlink failed */
109                 result = -1;
110                 r = 0;
111                 pb_log("%s: failed: unable to obtain temporary filename\n",
112                         __func__);
113         }
114         dest_filename[r] = '\0';
115
116         *destination_file = talloc_strdup(ctx, dest_filename);
117 out:
118         talloc_free(buffer);
119         fclose(source_handle);
120         fclose(destination_handle);
121         close(destination_fd);
122         return result;
123 }
124
125 int read_file(void *ctx, const char *filename, char **bufp, int *lenp)
126 {
127         struct stat statbuf;
128         int rc, fd, i, len;
129         char *buf;
130
131         fd = open(filename, O_RDONLY);
132         if (fd < 0)
133                 return -1;
134
135         rc = fstat(fd, &statbuf);
136         if (rc < 0)
137                 goto err_close;
138
139         len = statbuf.st_size;
140         if (len > max_file_size)
141                 goto err_close;
142
143         buf = talloc_array(ctx, char, len + 1);
144         if (!buf)
145                 goto err_close;
146
147         for (i = 0; i < len; i += rc) {
148                 rc = read(fd, buf + i, len - i);
149
150                 /* unexpected EOF: trim and return */
151                 if (rc == 0) {
152                         len = i;
153                         break;
154                 }
155
156                 if (rc < 0)
157                         goto err_free;
158
159         }
160
161         buf[len] = '\0';
162
163         close(fd);
164         *bufp = buf;
165         *lenp = len;
166         return 0;
167
168 err_free:
169         talloc_free(buf);
170 err_close:
171         close(fd);
172         return -1;
173 }
174
175 static int write_fd(int fd, char *buf, int len)
176 {
177         int i, rc;
178
179         for (i = 0; i < len; i += rc) {
180                 rc = write(fd, buf + i, len - i);
181                 if (rc < 0 && errno != -EINTR)
182                         return rc;
183         }
184
185         return 0;
186 }
187
188 int replace_file(const char *filename, char *buf, int len)
189 {
190         char *tempfile;
191         mode_t oldmask;
192         int rc, fd;
193
194         tempfile = talloc_asprintf(NULL, "%s.XXXXXX", filename);
195
196         oldmask = umask(0644);
197         fd = mkstemp(tempfile);
198         umask(oldmask);
199         if (fd < 0) {
200                 talloc_free(tempfile);
201                 return fd;
202         }
203
204         rc = write_fd(fd, buf, len);
205         if (rc) {
206                 unlink(tempfile);
207         } else {
208                 rc = rename(tempfile, filename);
209         }
210
211         talloc_free(tempfile);
212
213         fchmod(fd, 0644);
214
215         close(fd);
216         return rc;
217 }