]> git.ipfire.org Git - thirdparty/kmod.git/blob - libkmod/libkmod-util.c
util: Be OOM-safe and use _cleanup_free_
[thirdparty/kmod.git] / libkmod / libkmod-util.c
1 /*
2 * libkmod - interface to kernel module operations
3 *
4 * Copyright (C) 2011-2013 ProFUSION embedded systems
5 * Copyright (C) 2012 Lucas De Marchi <lucas.de.marchi@gmail.com>
6 * Copyright (C) 2013 Intel Corporation. All rights reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 #include <assert.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <stddef.h>
27 #include <stdarg.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <string.h>
31 #include <ctype.h>
32
33 #include "libkmod.h"
34 #include "libkmod-internal.h"
35
36 /*
37 * Read one logical line from a configuration file.
38 *
39 * Line endings may be escaped with backslashes, to form one logical line from
40 * several physical lines. No end of line character(s) are included in the
41 * result.
42 *
43 * If linenum is not NULL, it is incremented by the number of physical lines
44 * which have been read.
45 */
46 char *getline_wrapped(FILE *fp, unsigned int *linenum)
47 {
48 int size = 256;
49 int i = 0, n = 0;
50 _cleanup_free_ char *buf = malloc(size);
51
52 if (buf == NULL)
53 return NULL;
54
55 for(;;) {
56 int ch = getc_unlocked(fp);
57
58 switch(ch) {
59 case EOF:
60 if (i == 0)
61 return NULL;
62 /* else fall through */
63
64 case '\n':
65 n++;
66
67 {
68 char *ret;
69 if (i == size) {
70 ret = realloc(buf, size + 1);
71 if (!ret)
72 return NULL;
73 } else
74 ret = buf;
75 ret[i] = '\0';
76 buf = NULL;
77 if (linenum)
78 *linenum += n;
79 return ret;
80 }
81
82 case '\\':
83 ch = getc_unlocked(fp);
84
85 if (ch == '\n') {
86 n++;
87 continue;
88 }
89 /* else fall through */
90
91 default:
92 buf[i++] = ch;
93
94 if (i == size) {
95 char *tmp;
96 size *= 2;
97 tmp = realloc(buf, size);
98 if (!tmp)
99 return NULL;
100 buf = tmp;
101 }
102 }
103 }
104 }
105
106 inline int alias_normalize(const char *alias, char buf[PATH_MAX], size_t *len)
107 {
108 size_t s;
109
110 for (s = 0; s < PATH_MAX - 1; s++) {
111 const char c = alias[s];
112 switch (c) {
113 case '-':
114 buf[s] = '_';
115 break;
116 case ']':
117 return -EINVAL;
118 case '[':
119 while (alias[s] != ']' && alias[s] != '\0') {
120 buf[s] = alias[s];
121 s++;
122 }
123
124 if (alias[s] != ']')
125 return -EINVAL;
126
127 buf[s] = alias[s];
128 break;
129 case '\0':
130 goto finish;
131 default:
132 buf[s] = c;
133 }
134 }
135
136 finish:
137 buf[s] = '\0';
138
139 if (len)
140 *len = s;
141
142 return 0;
143 }
144
145 inline char *modname_normalize(const char *modname, char buf[PATH_MAX],
146 size_t *len)
147 {
148 size_t s;
149
150 for (s = 0; s < PATH_MAX - 1; s++) {
151 const char c = modname[s];
152 if (c == '-')
153 buf[s] = '_';
154 else if (c == '\0' || c == '.')
155 break;
156 else
157 buf[s] = c;
158 }
159
160 buf[s] = '\0';
161
162 if (len)
163 *len = s;
164
165 return buf;
166 }
167
168 char *path_to_modname(const char *path, char buf[PATH_MAX], size_t *len)
169 {
170 char *modname;
171
172 modname = basename(path);
173 if (modname == NULL || modname[0] == '\0')
174 return NULL;
175
176 return modname_normalize(modname, buf, len);
177 }
178
179 inline void *memdup(const void *p, size_t n)
180 {
181 void *r = malloc(n);
182
183 if (r == NULL)
184 return NULL;
185
186 return memcpy(r, p, n);
187 }
188
189 ssize_t read_str_safe(int fd, char *buf, size_t buflen)
190 {
191 size_t todo = buflen - 1;
192 size_t done = 0;
193
194 do {
195 ssize_t r = read(fd, buf + done, todo);
196
197 if (r == 0)
198 break;
199 else if (r > 0) {
200 todo -= r;
201 done += r;
202 } else {
203 if (errno == EAGAIN || errno == EWOULDBLOCK ||
204 errno == EINTR)
205 continue;
206 else
207 return -errno;
208 }
209 } while (todo > 0);
210
211 buf[done] = '\0';
212 return done;
213 }
214
215 ssize_t write_str_safe(int fd, const char *buf, size_t buflen)
216 {
217 size_t todo = buflen;
218 size_t done = 0;
219
220 do {
221 ssize_t r = write(fd, buf + done, todo);
222
223 if (r == 0)
224 break;
225 else if (r > 0) {
226 todo -= r;
227 done += r;
228 } else {
229 if (errno == EAGAIN || errno == EWOULDBLOCK ||
230 errno == EINTR)
231 continue;
232 else
233 return -errno;
234 }
235 } while (todo > 0);
236
237 return done;
238 }
239
240 int read_str_long(int fd, long *value, int base)
241 {
242 char buf[32], *end;
243 long v;
244 int err;
245
246 *value = 0;
247 err = read_str_safe(fd, buf, sizeof(buf));
248 if (err < 0)
249 return err;
250 errno = 0;
251 v = strtol(buf, &end, base);
252 if (end == buf || !isspace(*end))
253 return -EINVAL;
254
255 *value = v;
256 return 0;
257 }
258
259 int read_str_ulong(int fd, unsigned long *value, int base)
260 {
261 char buf[32], *end;
262 long v;
263 int err;
264
265 *value = 0;
266 err = read_str_safe(fd, buf, sizeof(buf));
267 if (err < 0)
268 return err;
269 errno = 0;
270 v = strtoul(buf, &end, base);
271 if (end == buf || !isspace(*end))
272 return -EINVAL;
273 *value = v;
274 return 0;
275 }
276
277 char *strchr_replace(char *s, int c, char r)
278 {
279 char *p;
280
281 for (p = s; *p != '\0'; p++)
282 if (*p == c)
283 *p = r;
284
285 return s;
286 }
287
288 bool path_is_absolute(const char *p)
289 {
290 assert(p != NULL);
291
292 return p[0] == '/';
293 }
294
295 char *path_make_absolute_cwd(const char *p)
296 {
297 _cleanup_free_ char *cwd = NULL;
298 size_t plen, cwdlen;
299 char *r;
300
301 if (path_is_absolute(p))
302 return strdup(p);
303
304 cwd = get_current_dir_name();
305 if (!cwd)
306 return NULL;
307
308 plen = strlen(p);
309 cwdlen = strlen(cwd);
310
311 /* cwd + '/' + p + '\0' */
312 r = realloc(cwd, cwdlen + 1 + plen + 1);
313 if (r == NULL)
314 return NULL;
315
316 cwd = NULL;
317 r[cwdlen] = '/';
318 memcpy(&r[cwdlen + 1], p, plen + 1);
319
320 return r;
321 }
322
323 static inline int is_dir(const char *path)
324 {
325 struct stat st;
326
327 if (stat(path, &st) >= 0)
328 return S_ISDIR(st.st_mode);
329
330 return -errno;
331 }
332
333 int mkdir_p(const char *path, int len, mode_t mode)
334 {
335 char *start, *end;
336
337 start = strndupa(path, len);
338 end = start + len;
339
340 /*
341 * scan backwards, replacing '/' with '\0' while the component doesn't
342 * exist
343 */
344 for (;;) {
345 int r = is_dir(start);
346 if (r > 0) {
347 end += strlen(end);
348
349 if (end == start + len)
350 return 0;
351
352 /* end != start, since it would be caught on the first
353 * iteration */
354 *end = '/';
355 break;
356 } else if (r == 0)
357 return -ENOTDIR;
358
359 if (end == start)
360 break;
361
362 *end = '\0';
363
364 /* Find the next component, backwards, discarding extra '/'*/
365 while (end > start && *end != '/')
366 end--;
367
368 while (end > start && *(end - 1) == '/')
369 end--;
370 }
371
372 for (; end < start + len;) {
373 if (mkdir(start, mode) < 0 && errno != EEXIST)
374 return -errno;
375
376 end += strlen(end);
377 *end = '/';
378 }
379
380 return 0;
381 }
382
383 int mkdir_parents(const char *path, mode_t mode)
384 {
385 char *end = strrchr(path, '/');
386
387 /* no parent directories */
388 if (end == NULL)
389 return 0;
390
391 return mkdir_p(path, end - path, mode);
392 }
393
394 const struct kmod_ext kmod_exts[] = {
395 {".ko", sizeof(".ko") - 1},
396 #ifdef ENABLE_ZLIB
397 {".ko.gz", sizeof(".ko.gz") - 1},
398 #endif
399 #ifdef ENABLE_XZ
400 {".ko.xz", sizeof(".ko.xz") - 1},
401 #endif
402 { }
403 };
404
405 bool path_ends_with_kmod_ext(const char *path, size_t len)
406 {
407 const struct kmod_ext *eitr;
408
409 for (eitr = kmod_exts; eitr->ext != NULL; eitr++) {
410 if (len <= eitr->len)
411 continue;
412 if (streq(path + len - eitr->len, eitr->ext))
413 return true;
414 }
415
416 return false;
417 }
418
419 #define USEC_PER_SEC 1000000ULL
420 #define NSEC_PER_USEC 1000ULL
421 unsigned long long ts_usec(const struct timespec *ts)
422 {
423 return (unsigned long long) ts->tv_sec * USEC_PER_SEC +
424 (unsigned long long) ts->tv_nsec / NSEC_PER_USEC;
425 }
426
427 unsigned long long stat_mstamp(const struct stat *st)
428 {
429 #ifdef HAVE_STRUCT_STAT_ST_MTIM
430 return ts_usec(&st->st_mtim);
431 #else
432 return (unsigned long long) st->st_mtime;
433 #endif
434 }