blob: c42c31225e149262dbb4f938dfc1413e8be43bae [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 Hughes94f939a2017-08-08 12:21:39 +010025#include <archive_entry.h>
26#include <archive.h>
Richard Hughes943d2c92017-06-21 09:04:39 +010027
28#include "fwupd-error.h"
29
30#include "fu-common.h"
31
32/**
33 * fu_common_set_contents_bytes:
34 * @filename: A filename
35 * @bytes: The data to write
36 * @error: A #GError, or %NULL
37 *
38 * Writes a blob of data to a filename, creating the parent directories as
39 * required.
40 *
41 * Returns: %TRUE for success
42 **/
43gboolean
44fu_common_set_contents_bytes (const gchar *filename, GBytes *bytes, GError **error)
45{
46 const gchar *data;
47 gsize size;
48 g_autoptr(GFile) file = NULL;
49 g_autoptr(GFile) file_parent = NULL;
50
51 file = g_file_new_for_path (filename);
52 file_parent = g_file_get_parent (file);
53 if (!g_file_query_exists (file_parent, NULL)) {
54 if (!g_file_make_directory_with_parents (file_parent, NULL, error))
55 return FALSE;
56 }
57 data = g_bytes_get_data (bytes, &size);
58 return g_file_set_contents (filename, data, size, error);
59}
60
Richard Hughesd0d2ae62017-08-08 12:22:30 +010061/**
62 * fu_common_get_contents_bytes:
63 * @filename: A filename
64 * @error: A #GError, or %NULL
65 *
66 * Reads a blob of data from a file.
67 *
68 * Returns: a #GBytes, or %NULL for failure
69 **/
70GBytes *
71fu_common_get_contents_bytes (const gchar *filename, GError **error)
72{
73 gchar *data = NULL;
74 gsize len = 0;
75 if (!g_file_get_contents (filename, &data, &len, error))
76 return NULL;
77 return g_bytes_new_take (data, len);
78}
Richard Hughes943d2c92017-06-21 09:04:39 +010079
80/**
81 * fu_common_get_contents_fd:
82 * @fd: A file descriptor
83 * @count: The maximum number of bytes to read
84 * @error: A #GError, or %NULL
85 *
86 * Reads a blob from a specific file descriptor.
87 *
88 * Note: this will close the fd when done
89 *
90 * Returns: (transfer container): a #GBytes, or %NULL
91 **/
92GBytes *
93fu_common_get_contents_fd (gint fd, gsize count, GError **error)
94{
95 g_autoptr(GBytes) blob = NULL;
96 g_autoptr(GError) error_local = NULL;
97 g_autoptr(GInputStream) stream = NULL;
98
99 g_return_val_if_fail (fd > 0, NULL);
100 g_return_val_if_fail (count > 0, NULL);
101 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
102
103 /* read the entire fd to a data blob */
104 stream = g_unix_input_stream_new (fd, TRUE);
105 blob = g_input_stream_read_bytes (stream, count, NULL, &error_local);
106 if (blob == NULL) {
107 g_set_error_literal (error,
108 FWUPD_ERROR,
109 FWUPD_ERROR_INVALID_FILE,
110 error_local->message);
111 return NULL;
112 }
113 return g_steal_pointer (&blob);
114}
Richard Hughes94f939a2017-08-08 12:21:39 +0100115
116static gboolean
117fu_common_extract_archive_entry (struct archive_entry *entry, const gchar *dir)
118{
119 const gchar *tmp;
120 g_autofree gchar *buf = NULL;
121
122 /* no output file */
123 if (archive_entry_pathname (entry) == NULL)
124 return FALSE;
125
126 /* update output path */
127 tmp = archive_entry_pathname (entry);
128 buf = g_build_filename (dir, tmp, NULL);
129 archive_entry_update_pathname_utf8 (entry, buf);
130 return TRUE;
131}
132
133/**
134 * fu_common_extract_archive:
135 * @blob: a #GBytes archive as a blob
136 * @directory: a directory name to extract to
137 * @error: A #GError, or %NULL
138 *
139 * Extracts an achive to a directory.
140 *
141 * Returns: %TRUE for success
142 **/
143gboolean
144fu_common_extract_archive (GBytes *blob, const gchar *dir, GError **error)
145{
146 gboolean ret = TRUE;
147 int r;
148 struct archive *arch = NULL;
149 struct archive_entry *entry;
150
151 /* decompress anything matching either glob */
152 arch = archive_read_new ();
153 archive_read_support_format_all (arch);
154 archive_read_support_filter_all (arch);
155 r = archive_read_open_memory (arch,
156 (void *) g_bytes_get_data (blob, NULL),
157 (size_t) g_bytes_get_size (blob));
158 if (r != 0) {
159 ret = FALSE;
160 g_set_error (error,
161 FWUPD_ERROR,
162 FWUPD_ERROR_INTERNAL,
163 "Cannot open: %s",
164 archive_error_string (arch));
165 goto out;
166 }
167 for (;;) {
168 gboolean valid;
169 g_autofree gchar *path = NULL;
170 r = archive_read_next_header (arch, &entry);
171 if (r == ARCHIVE_EOF)
172 break;
173 if (r != ARCHIVE_OK) {
174 ret = FALSE;
175 g_set_error (error,
176 FWUPD_ERROR,
177 FWUPD_ERROR_INTERNAL,
178 "Cannot read header: %s",
179 archive_error_string (arch));
180 goto out;
181 }
182
183 /* only extract if valid */
184 valid = fu_common_extract_archive_entry (entry, dir);
185 if (!valid)
186 continue;
187 r = archive_read_extract (arch, entry, 0);
188 if (r != ARCHIVE_OK) {
189 ret = FALSE;
190 g_set_error (error,
191 FWUPD_ERROR,
192 FWUPD_ERROR_INTERNAL,
193 "Cannot extract: %s",
194 archive_error_string (arch));
195 goto out;
196 }
197 }
198out:
199 if (arch != NULL) {
200 archive_read_close (arch);
201 archive_read_free (arch);
202 }
203 return ret;
204}