blob: 353908bcaef0bbbf09fe25d1bab0323e95fa88fd [file] [log] [blame]
Richard Hughes943d2c92017-06-21 09:04:39 +01001/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
4 *
5 * Licensed under the GNU General Public License Version 2
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22#include <config.h>
23
24#include <gio/gunixinputstream.h>
Richard Hughes954dd9f2017-08-08 13:36:25 +010025#include <glib/gstdio.h>
26
Richard Hughes94f939a2017-08-08 12:21:39 +010027#include <archive_entry.h>
28#include <archive.h>
Richard Hughes943d2c92017-06-21 09:04:39 +010029
30#include "fwupd-error.h"
31
32#include "fu-common.h"
33
34/**
Richard Hughes954dd9f2017-08-08 13:36:25 +010035 * fu_common_rmtree:
36 * @directory: a directory name
37 * @error: A #GError or %NULL
38 *
39 * Recursively removes a directory.
40 *
41 * Returns: %TRUE for success, %FALSE otherwise
42 **/
43gboolean
44fu_common_rmtree (const gchar *directory, GError **error)
45{
46 const gchar *filename;
47 g_autoptr(GDir) dir = NULL;
48
49 /* try to open */
50 dir = g_dir_open (directory, 0, error);
51 if (dir == NULL)
52 return FALSE;
53
54 /* find each */
55 while ((filename = g_dir_read_name (dir))) {
56 g_autofree gchar *src = NULL;
57 src = g_build_filename (directory, filename, NULL);
58 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
59 if (!fu_common_rmtree (src, error))
60 return FALSE;
61 } else {
62 if (g_unlink (src) != 0) {
63 g_set_error (error,
64 FWUPD_ERROR,
65 FWUPD_ERROR_INTERNAL,
66 "Failed to delete: %s", src);
67 return FALSE;
68 }
69 }
70 }
71 if (g_remove (directory) != 0) {
72 g_set_error (error,
73 FWUPD_ERROR,
74 FWUPD_ERROR_INTERNAL,
75 "Failed to delete: %s", directory);
76 return FALSE;
77 }
78 return TRUE;
79}
80
81/**
Richard Hughes943d2c92017-06-21 09:04:39 +010082 * fu_common_set_contents_bytes:
83 * @filename: A filename
84 * @bytes: The data to write
85 * @error: A #GError, or %NULL
86 *
87 * Writes a blob of data to a filename, creating the parent directories as
88 * required.
89 *
90 * Returns: %TRUE for success
91 **/
92gboolean
93fu_common_set_contents_bytes (const gchar *filename, GBytes *bytes, GError **error)
94{
95 const gchar *data;
96 gsize size;
97 g_autoptr(GFile) file = NULL;
98 g_autoptr(GFile) file_parent = NULL;
99
100 file = g_file_new_for_path (filename);
101 file_parent = g_file_get_parent (file);
102 if (!g_file_query_exists (file_parent, NULL)) {
103 if (!g_file_make_directory_with_parents (file_parent, NULL, error))
104 return FALSE;
105 }
106 data = g_bytes_get_data (bytes, &size);
107 return g_file_set_contents (filename, data, size, error);
108}
109
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100110/**
111 * fu_common_get_contents_bytes:
112 * @filename: A filename
113 * @error: A #GError, or %NULL
114 *
115 * Reads a blob of data from a file.
116 *
117 * Returns: a #GBytes, or %NULL for failure
118 **/
119GBytes *
120fu_common_get_contents_bytes (const gchar *filename, GError **error)
121{
122 gchar *data = NULL;
123 gsize len = 0;
124 if (!g_file_get_contents (filename, &data, &len, error))
125 return NULL;
126 return g_bytes_new_take (data, len);
127}
Richard Hughes943d2c92017-06-21 09:04:39 +0100128
129/**
130 * fu_common_get_contents_fd:
131 * @fd: A file descriptor
132 * @count: The maximum number of bytes to read
133 * @error: A #GError, or %NULL
134 *
135 * Reads a blob from a specific file descriptor.
136 *
137 * Note: this will close the fd when done
138 *
139 * Returns: (transfer container): a #GBytes, or %NULL
140 **/
141GBytes *
142fu_common_get_contents_fd (gint fd, gsize count, GError **error)
143{
144 g_autoptr(GBytes) blob = NULL;
145 g_autoptr(GError) error_local = NULL;
146 g_autoptr(GInputStream) stream = NULL;
147
148 g_return_val_if_fail (fd > 0, NULL);
149 g_return_val_if_fail (count > 0, NULL);
150 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
151
152 /* read the entire fd to a data blob */
153 stream = g_unix_input_stream_new (fd, TRUE);
154 blob = g_input_stream_read_bytes (stream, count, NULL, &error_local);
155 if (blob == NULL) {
156 g_set_error_literal (error,
157 FWUPD_ERROR,
158 FWUPD_ERROR_INVALID_FILE,
159 error_local->message);
160 return NULL;
161 }
162 return g_steal_pointer (&blob);
163}
Richard Hughes94f939a2017-08-08 12:21:39 +0100164
165static gboolean
166fu_common_extract_archive_entry (struct archive_entry *entry, const gchar *dir)
167{
168 const gchar *tmp;
169 g_autofree gchar *buf = NULL;
170
171 /* no output file */
172 if (archive_entry_pathname (entry) == NULL)
173 return FALSE;
174
175 /* update output path */
176 tmp = archive_entry_pathname (entry);
177 buf = g_build_filename (dir, tmp, NULL);
178 archive_entry_update_pathname_utf8 (entry, buf);
179 return TRUE;
180}
181
182/**
183 * fu_common_extract_archive:
184 * @blob: a #GBytes archive as a blob
185 * @directory: a directory name to extract to
186 * @error: A #GError, or %NULL
187 *
188 * Extracts an achive to a directory.
189 *
190 * Returns: %TRUE for success
191 **/
192gboolean
193fu_common_extract_archive (GBytes *blob, const gchar *dir, GError **error)
194{
195 gboolean ret = TRUE;
196 int r;
197 struct archive *arch = NULL;
198 struct archive_entry *entry;
199
200 /* decompress anything matching either glob */
201 arch = archive_read_new ();
202 archive_read_support_format_all (arch);
203 archive_read_support_filter_all (arch);
204 r = archive_read_open_memory (arch,
205 (void *) g_bytes_get_data (blob, NULL),
206 (size_t) g_bytes_get_size (blob));
207 if (r != 0) {
208 ret = FALSE;
209 g_set_error (error,
210 FWUPD_ERROR,
211 FWUPD_ERROR_INTERNAL,
212 "Cannot open: %s",
213 archive_error_string (arch));
214 goto out;
215 }
216 for (;;) {
217 gboolean valid;
218 g_autofree gchar *path = NULL;
219 r = archive_read_next_header (arch, &entry);
220 if (r == ARCHIVE_EOF)
221 break;
222 if (r != ARCHIVE_OK) {
223 ret = FALSE;
224 g_set_error (error,
225 FWUPD_ERROR,
226 FWUPD_ERROR_INTERNAL,
227 "Cannot read header: %s",
228 archive_error_string (arch));
229 goto out;
230 }
231
232 /* only extract if valid */
233 valid = fu_common_extract_archive_entry (entry, dir);
234 if (!valid)
235 continue;
236 r = archive_read_extract (arch, entry, 0);
237 if (r != ARCHIVE_OK) {
238 ret = FALSE;
239 g_set_error (error,
240 FWUPD_ERROR,
241 FWUPD_ERROR_INTERNAL,
242 "Cannot extract: %s",
243 archive_error_string (arch));
244 goto out;
245 }
246 }
247out:
248 if (arch != NULL) {
249 archive_read_close (arch);
250 archive_read_free (arch);
251 }
252 return ret;
253}