1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * Copyright (C) 2013-2015 Richard Hughes <richard@hughsie.com>
4 *
5 * Licensed under the GNU Lesser General Public License Version 2.1
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or(at your option) any later version.
11 *
12 * This library 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 GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 /**
23 * SECTION:dnf-utils
24 * @short_description: Helper functions for libdnf
25 * @include: libdnf.h
26 * @stability: Unstable
27 *
28 * These functions are used internally in libdnf for various things.
29 */
30
31
32 #include <errno.h>
33 #include <stdlib.h>
34 #include <glib/gstdio.h>
35
36 #include "catch-error.hpp"
37 #include "dnf-types.h"
38 #include "dnf-utils.h"
39
40 #include "utils/bgettext/bgettext-lib.h"
41
42 /**
43 * dnf_error_quark:
44 *
45 * Returns a #GQuark for the error domain used in libdnf
46 *
47 * Returns: an error quark
48 **/
49 GQuark
dnf_error_quark(void)50 dnf_error_quark(void)
51 {
52 static GQuark quark = 0;
53 if (!quark)
54 quark = g_quark_from_static_string("DnfError");
55 return quark;
56 }
57
58 /**
59 * dnf_realpath:
60 * @path: A relative path, e.g. "../../data/test"
61 *
62 * Converts relative paths to absolute ones.
63 *
64 * Returns: a new path, or %NULL
65 *
66 * Since: 0.1.7
67 **/
68 gchar *
dnf_realpath(const gchar * path)69 dnf_realpath(const gchar *path)
70 {
71 gchar *real = NULL;
72 char *temp;
73
74 /* don't trust realpath one little bit */
75 if (path == NULL)
76 return NULL;
77
78 /* glibc allocates us a buffer to try and fix some brain damage */
79 temp = realpath(path, NULL);
80 if (temp == NULL)
81 return NULL;
82 real = g_strdup(temp);
83 free(temp);
84 return real;
85 }
86
87 /**
88 * dnf_remove_recursive:
89 * @directory: A directory path
90 * @error: A #GError, or %NULL
91 *
92 * Removes a directory and its contents. Use with care.
93 *
94 * Returns: %FALSE if an error was set
95 *
96 * Since: 0.1.7
97 **/
98 gboolean
dnf_remove_recursive(const gchar * directory,GError ** error)99 dnf_remove_recursive(const gchar *directory, GError **error) try
100 {
101 const gchar *filename;
102 g_autoptr(GDir) dir = NULL;
103 g_autoptr(GError) error_local = NULL;
104
105 /* try to open */
106 dir = g_dir_open(directory, 0, &error_local);
107 if (dir == NULL) {
108 g_set_error(error,
109 DNF_ERROR,
110 DNF_ERROR_INTERNAL_ERROR,
111 _("cannot open directory %1$s: %2$s"),
112 directory, error_local->message);
113 return FALSE;
114 }
115
116 /* find each */
117 while ((filename = g_dir_read_name(dir))) {
118 g_autofree gchar *src = NULL;
119 src = g_build_filename(directory, filename, NULL);
120 if (g_file_test(src, G_FILE_TEST_IS_DIR)) {
121 if (!dnf_remove_recursive(src, error))
122 return FALSE;
123 } else {
124 g_debug("deleting file %s", src);
125 if (!dnf_ensure_file_unlinked(src, error))
126 return FALSE;
127 }
128 }
129
130 /* remove directory */
131 g_debug("deleting directory %s", directory);
132 if (g_remove(directory) != 0) {
133 g_set_error(error,
134 DNF_ERROR,
135 DNF_ERROR_INTERNAL_ERROR,
136 _("failed to remove %s"), directory);
137 return FALSE;
138 }
139 return TRUE;
140 } CATCH_TO_GERROR(FALSE)
141
142
143 /**
144 * dnf_ensure_file_unlinked:
145 * @src_path: the path to the file
146 * @error: A #GError, or %NULL
147 *
148 * Remove a file based on the file path,
149 * refactored from dnf_remove_recursive() function
150 *
151 * Returns: %FALSE if an error was set
152 *
153 * Since 0.9.4
154 **/
155 gboolean
156 dnf_ensure_file_unlinked(const gchar *src_path, GError **error) try
157 {
158 if ((unlink(src_path) != 0) && errno != ENOENT) {
159 g_set_error(error,
160 DNF_ERROR,
161 DNF_ERROR_INTERNAL_ERROR,
162 "failed to unlink %s", src_path);
163 return FALSE;
164 }
165
166 return TRUE;
CATCH_TO_GERROR(FALSE)167 } CATCH_TO_GERROR(FALSE)
168
169 /**
170 * dnf_delete_files_matching:
171 * @directory_path: the top level directory path to look at
172 * @patterns: the patterns that we are expecting from file/directory's name
173 * @error: a #GError instance, to track error
174 *
175 * Remove recursively all the files/directories that have names
176 * which match the patterns. Use with care
177 *
178 * There are several assumptions that are made in this function:
179 *
180 * 1: We assume the top level path( the path passed in initially)
181 * does not satisfy the criteria of matching the pattern
182 *
183 * 2: We assume the top level path itself is a directory initially
184 *
185 * Returns: %FALSE if failed to delete a file/directory
186 *
187 * Since: 0.9.4
188 **/
189 gboolean
190 dnf_delete_files_matching(const gchar* directory_path,
191 const char* const* patterns,
192 GError **error) try
193 {
194 const gchar *filename;
195 g_autoptr(GDir) dir = NULL;
196
197 /* try to open the directory*/
198 dir = g_dir_open(directory_path, 0, error);
199 if (dir == NULL) {
200 g_prefix_error(error, "Cannot open directory %s: ", directory_path);
201 return FALSE;
202 }
203 /* In the directory, we read each file and check if their name matches one of the patterns */
204 while ((filename = g_dir_read_name(dir))) {
205 g_autofree gchar *src = g_build_filename(directory_path, filename, NULL);
206 if (g_file_test(src, G_FILE_TEST_IS_DIR)) {
207 gboolean matching = FALSE;
208 for (char **iter = (char **) patterns; iter && *iter; iter++) {
209 const char* pattern = *iter;
210 if (g_str_has_suffix(filename, pattern)) {
211 if (!dnf_remove_recursive(src, error))
212 return FALSE;
213 matching = TRUE;
214 break;
215 }
216 }
217 if (!matching) {
218 /* If the directory does not match pattern, keep looking into it */
219 if (!dnf_delete_files_matching(src, patterns, error))
220 return FALSE;
221 }
222 }
223 else {
224 /* This is for files in the directory, if matches, we directly delete it */
225 for (char **iter = (char **)patterns; iter && *iter; iter++) {
226 const char* pattern = *iter;
227 if (g_str_has_suffix(filename, pattern)) {
228 if (!dnf_ensure_file_unlinked(src, error))
229 return FALSE;
230 break;
231 }
232 }
233 }
234 }
235
236 return TRUE;
237 } CATCH_TO_GERROR(FALSE)
238
239 /**
240 * dnf_get_file_contents_allow_noent:
241 * @path: File to open
242 * @out_contents: Output variable containing contents
243 * @out_length: Output variable containing length
244 * @error: A #GError, or %NULL
245 *
246 * Reads a file's contents. If the file does not exist, returns TRUE.
247 *
248 * Returns: %FALSE if an error other than G_FILE_ERROR_NOENT was set
249 *
250 * Since: 0.1.7
251 **/
252 gboolean
253 dnf_get_file_contents_allow_noent(const gchar *path,
254 gchar **out_contents,
255 gsize *out_length,
256 GError **error) try
257 {
258 gsize length;
259 g_autofree gchar *contents = NULL;
260 g_autoptr(GError) local_error = NULL;
261
262 if (!g_file_get_contents(path, &contents, &length, &local_error)) {
263 if (g_error_matches(local_error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
264 return TRUE;
265
266 g_propagate_error(error, local_error);
267 return FALSE;
268 }
269
270 if (out_contents != NULL)
271 *out_contents = static_cast<gchar *>(g_steal_pointer (&contents));
272 if (out_length != NULL)
273 *out_length = length;
274
275 return TRUE;
276 } CATCH_TO_GERROR(FALSE)
277