lib/log: Switch to pb_log_fn
[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 + 1] = "";
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
48         source_handle = fopen(source_file, "r");
49         if (!source_handle) {
50                 pb_log("%s: unable to open source file '%s': %m\n",
51                         __func__, source_file);
52                         return -1;
53         }
54
55         destination_fd = mkstemp(template);
56         if (destination_fd < 0) {
57                 pb_log_fn("unable to create temp file, %m\n");
58                 fclose(source_handle);
59                 return -1;
60         }
61         destination_handle = fdopen(destination_fd, "w");
62         if (!destination_handle) {
63                 pb_log_fn("unable to open destination file, %m\n");
64                 fclose(source_handle);
65                 close(destination_fd);
66                 return -1;
67         }
68
69         buffer = talloc_array(ctx, unsigned char, FILE_XFER_BUFFER_SIZE);
70         if (!buffer) {
71                 pb_log("%s: failed: unable to allocate file transfer buffer\n",
72                         __func__);
73                 result = -1;
74                 goto out;
75         }
76
77         /* Copy data */
78         while ((l1 = fread(buffer, 1, FILE_XFER_BUFFER_SIZE, source_handle)) > 0) {
79                 size_t l2 = fwrite(buffer, 1, l1, destination_handle);
80                 if (l2 < l1) {
81                         if (ferror(destination_handle)) {
82                                 /* General error */
83                                 result = -1;
84                                 pb_log_fn("failed: unknown fault\n");
85                         }
86                         else {
87                                 /* No space on destination device */
88                                 result = -1;
89                                 pb_log("%s: failed: temporary storage full\n",
90                                         __func__);
91                         }
92                         break;
93                 }
94         }
95
96         if (result) {
97                 *destination_file = NULL;
98                 goto out;
99         }
100
101         snprintf(readlink_buffer, MAX_FILENAME_SIZE, "/proc/self/fd/%d",
102                 destination_fd);
103         r = readlink(readlink_buffer, dest_filename, MAX_FILENAME_SIZE);
104         if (r < 0) {
105                 /* readlink failed */
106                 result = -1;
107                 r = 0;
108                 pb_log("%s: failed: unable to obtain temporary filename\n",
109                         __func__);
110         }
111         dest_filename[r] = '\0';
112
113         *destination_file = talloc_strdup(ctx, dest_filename);
114 out:
115         talloc_free(buffer);
116         fclose(source_handle);
117         fclose(destination_handle);
118         close(destination_fd);
119         return result;
120 }
121
122 int read_file(void *ctx, const char *filename, char **bufp, int *lenp)
123 {
124         struct stat statbuf;
125         int rc, fd, i, len;
126         char *buf;
127
128         fd = open(filename, O_RDONLY);
129         if (fd < 0)
130                 return -1;
131
132         rc = fstat(fd, &statbuf);
133         if (rc < 0)
134                 goto err_close;
135
136         len = statbuf.st_size;
137         if (len > max_file_size)
138                 goto err_close;
139
140         buf = talloc_array(ctx, char, len + 1);
141         if (!buf)
142                 goto err_close;
143
144         for (i = 0; i < len; i += rc) {
145                 rc = read(fd, buf + i, len - i);
146
147                 /* unexpected EOF: trim and return */
148                 if (rc == 0) {
149                         len = i;
150                         break;
151                 }
152
153                 if (rc < 0)
154                         goto err_free;
155
156         }
157
158         buf[len] = '\0';
159
160         close(fd);
161         *bufp = buf;
162         *lenp = len;
163         return 0;
164
165 err_free:
166         talloc_free(buf);
167 err_close:
168         close(fd);
169         return -1;
170 }
171
172 static int write_fd(int fd, char *buf, int len)
173 {
174         int i, rc;
175
176         for (i = 0; i < len; i += rc) {
177                 rc = write(fd, buf + i, len - i);
178                 if (rc < 0 && errno != -EINTR)
179                         return rc;
180         }
181
182         return 0;
183 }
184
185 int replace_file(const char *filename, char *buf, int len)
186 {
187         char *tempfile;
188         mode_t oldmask;
189         int rc, fd;
190
191         tempfile = talloc_asprintf(NULL, "%s.XXXXXX", filename);
192
193         oldmask = umask(0644);
194         fd = mkstemp(tempfile);
195         umask(oldmask);
196         if (fd < 0) {
197                 talloc_free(tempfile);
198                 return fd;
199         }
200
201         rc = write_fd(fd, buf, len);
202         if (rc) {
203                 unlink(tempfile);
204         } else {
205                 rc = rename(tempfile, filename);
206         }
207
208         talloc_free(tempfile);
209
210         fchmod(fd, 0644);
211
212         close(fd);
213         return rc;
214 }