/src/util-linux/libmount/src/tab_parse.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
2 | | /* |
3 | | * This file is part of libmount from util-linux project. |
4 | | * |
5 | | * Copyright (C) 2009-2018 Karel Zak <kzak@redhat.com> |
6 | | * |
7 | | * libmount is free software; you can redistribute it and/or modify it |
8 | | * under the terms of the GNU Lesser General Public License as published by |
9 | | * the Free Software Foundation; either version 2.1 of the License, or |
10 | | * (at your option) any later version. |
11 | | */ |
12 | | #ifdef HAVE_SCANDIRAT |
13 | | #ifndef __USE_GNU |
14 | | #define __USE_GNU |
15 | | #endif /* !__USE_GNU */ |
16 | | #endif /* HAVE_SCANDIRAT */ |
17 | | |
18 | | #include <ctype.h> |
19 | | #include <limits.h> |
20 | | #include <dirent.h> |
21 | | #include <fcntl.h> |
22 | | #include <stdlib.h> |
23 | | #include <string.h> |
24 | | #include <sys/stat.h> |
25 | | |
26 | | #include "fileutils.h" |
27 | | #include "mangle.h" |
28 | | #include "mountP.h" |
29 | | #include "pathnames.h" |
30 | | #include "strutils.h" |
31 | | |
32 | | struct libmnt_parser { |
33 | | FILE *f; /* fstab, swaps or mountinfo ... */ |
34 | | const char *filename; /* file name or NULL */ |
35 | | char *buf; /* buffer (the current line content) */ |
36 | | size_t bufsiz; /* size of the buffer */ |
37 | | size_t line; /* current line */ |
38 | | int sysroot_rc; /* rc from mnt_guess_system_root() */ |
39 | | char *sysroot; /* guess from mmnt_guess_system_root() */ |
40 | | }; |
41 | | |
42 | | static void parser_cleanup(struct libmnt_parser *pa) |
43 | 3.39k | { |
44 | 3.39k | if (!pa) |
45 | 0 | return; |
46 | 3.39k | free(pa->buf); |
47 | 3.39k | free(pa->sysroot); |
48 | 3.39k | memset(pa, 0, sizeof(*pa)); |
49 | 3.39k | } |
50 | | |
51 | | static const char *next_s32(const char *s, int *num, int *rc) |
52 | 25.6k | { |
53 | 25.6k | char *end = NULL; |
54 | | |
55 | 25.6k | if (!s || !*s) |
56 | 1.17k | return s; |
57 | | |
58 | 24.4k | errno = 0; |
59 | 24.4k | *rc = -EINVAL; |
60 | 24.4k | *num = strtol(s, &end, 10); |
61 | 24.4k | if (end == NULL || s == end) |
62 | 6.84k | return s; |
63 | 17.6k | if (errno == 0 && (*end == ' ' || *end == '\t' || *end == '\0')) |
64 | 16.0k | *rc = 0; |
65 | 17.6k | return end; |
66 | 24.4k | } |
67 | | |
68 | | static const char *next_u64(const char *s, uint64_t *num, int *rc) |
69 | 6.13k | { |
70 | 6.13k | char *end = NULL; |
71 | | |
72 | 6.13k | if (!s || !*s) |
73 | 873 | return s; |
74 | | |
75 | 5.26k | errno = 0; |
76 | 5.26k | *rc = -EINVAL; |
77 | 5.26k | *num = (uint64_t) strtoumax(s, &end, 10); |
78 | 5.26k | if (end == NULL || s == end) |
79 | 673 | return s; |
80 | 4.58k | if (errno == 0 && (*end == ' ' || *end == '\t' || *end == '\0')) |
81 | 3.62k | *rc = 0; |
82 | 4.58k | return end; |
83 | 5.26k | } |
84 | | |
85 | | static inline const char *skip_separator(const char *p) |
86 | 216k | { |
87 | 389k | while (p && (*p == ' ' || *p == '\t')) |
88 | 173k | ++p; |
89 | 216k | return p; |
90 | 216k | } |
91 | | |
92 | | static inline const char *skip_nonspearator(const char *p) |
93 | 5.08k | { |
94 | 15.6k | while (p && *p && !(*p == ' ' || *p == '\t')) |
95 | 10.5k | p++; |
96 | 5.08k | return p; |
97 | 5.08k | } |
98 | | |
99 | | /* |
100 | | * Parses one line from {fs,m}tab |
101 | | */ |
102 | | static int mnt_parse_table_line(struct libmnt_fs *fs, const char *s) |
103 | 55.6k | { |
104 | 55.6k | int rc = 0; |
105 | 55.6k | char *p = NULL; |
106 | | |
107 | 55.6k | fs->passno = fs->freq = 0; |
108 | | |
109 | | /* (1) source */ |
110 | 55.6k | p = unmangle(s, &s); |
111 | 55.6k | if (!p || (rc = __mnt_fs_set_source_ptr(fs, p))) { |
112 | 0 | DBG(TAB, ul_debug("tab parse error: [source]")); |
113 | 0 | free(p); |
114 | 0 | goto fail; |
115 | 0 | } |
116 | | |
117 | 55.6k | s = skip_separator(s); |
118 | | |
119 | | /* (2) target */ |
120 | 55.6k | fs->target = unmangle(s, &s); |
121 | 55.6k | if (!fs->target) { |
122 | 5.90k | DBG(TAB, ul_debug("tab parse error: [target]")); |
123 | 5.90k | goto fail; |
124 | 5.90k | } |
125 | | |
126 | 49.7k | s = skip_separator(s); |
127 | | |
128 | | /* (3) FS type */ |
129 | 49.7k | p = unmangle(s, &s); |
130 | 49.7k | if (!p || (rc = __mnt_fs_set_fstype_ptr(fs, p))) { |
131 | 1.07k | DBG(TAB, ul_debug("tab parse error: [fstype]")); |
132 | 1.07k | free(p); |
133 | 1.07k | goto fail; |
134 | 1.07k | } |
135 | | |
136 | 48.6k | s = skip_separator(s); |
137 | | |
138 | | /* (4) options (optional) */ |
139 | 48.6k | p = unmangle(s, &s); |
140 | 48.6k | if (p && (rc = mnt_fs_set_options(fs, p))) { |
141 | 0 | DBG(TAB, ul_debug("tab parse error: [options]")); |
142 | 0 | free(p); |
143 | 0 | goto fail; |
144 | 0 | } |
145 | 48.6k | if (!p) |
146 | 36.6k | goto done; |
147 | 11.9k | free(p); |
148 | | |
149 | 11.9k | s = skip_separator(s); |
150 | 11.9k | if (!s || !*s) |
151 | 4.05k | goto done; |
152 | | |
153 | | /* (5) freq (optional) */ |
154 | 7.93k | s = next_s32(s, &fs->freq, &rc); |
155 | 7.93k | if (s && *s && rc) { |
156 | 5.73k | DBG(TAB, ul_debug("tab parse error: [freq]")); |
157 | 5.73k | goto fail; |
158 | 5.73k | } |
159 | | |
160 | 2.20k | s = skip_separator(s); |
161 | 2.20k | if (!s || !*s) |
162 | 968 | goto done; |
163 | | |
164 | | /* (6) passno (optional) */ |
165 | 1.23k | s = next_s32(s, &fs->passno, &rc); |
166 | 1.23k | if (s && *s && rc) { |
167 | 475 | DBG(TAB, ul_debug("tab parse error: [passno]")); |
168 | 475 | goto fail; |
169 | 475 | } |
170 | | |
171 | 42.4k | done: |
172 | 42.4k | return 0; |
173 | 13.1k | fail: |
174 | 13.1k | if (rc == 0) |
175 | 6.98k | rc = -EINVAL; |
176 | 13.1k | DBG(TAB, ul_debug("tab parse error on: '%s' [rc=%d]", s, rc)); |
177 | 13.1k | return rc; |
178 | 1.23k | } |
179 | | |
180 | | |
181 | | /* |
182 | | * Parses one line from a mountinfo file |
183 | | */ |
184 | | static int mnt_parse_mountinfo_line(struct libmnt_fs *fs, const char *s) |
185 | 8.47k | { |
186 | 8.47k | int rc = 0; |
187 | 8.47k | unsigned int maj, min; |
188 | 8.47k | char *p; |
189 | | |
190 | 8.47k | fs->flags |= MNT_FS_KERNEL; |
191 | | |
192 | | /* (1) id */ |
193 | 8.47k | s = next_s32(s, &fs->id, &rc); |
194 | 8.47k | if (!s || !*s || rc) { |
195 | 1.43k | DBG(TAB, ul_debug("tab parse error: [id]")); |
196 | 1.43k | goto fail; |
197 | 1.43k | } |
198 | | |
199 | 7.04k | s = skip_separator(s); |
200 | | |
201 | | /* (2) parent */ |
202 | 7.04k | s = next_s32(s, &fs->parent, &rc); |
203 | 7.04k | if (!s || !*s || rc) { |
204 | 1.52k | DBG(TAB, ul_debug("tab parse error: [parent]")); |
205 | 1.52k | goto fail; |
206 | 1.52k | } |
207 | | |
208 | 5.52k | s = skip_separator(s); |
209 | | |
210 | | /* (3) maj:min */ |
211 | 5.52k | if (sscanf(s, "%u:%u", &maj, &min) != 2) { |
212 | 436 | DBG(TAB, ul_debug("tab parse error: [maj:min]")); |
213 | 436 | goto fail; |
214 | 436 | } |
215 | 5.08k | fs->devno = makedev(maj, min); |
216 | 5.08k | s = skip_nonspearator(s); |
217 | 5.08k | s = skip_separator(s); |
218 | | |
219 | | /* (4) mountroot */ |
220 | 5.08k | fs->root = unmangle(s, &s); |
221 | 5.08k | if (!fs->root) { |
222 | 228 | DBG(TAB, ul_debug("tab parse error: [mountroot]")); |
223 | 228 | goto fail; |
224 | 228 | } |
225 | | |
226 | 4.85k | s = skip_separator(s); |
227 | | |
228 | | /* (5) target */ |
229 | 4.85k | fs->target = unmangle(s, &s); |
230 | 4.85k | if (!fs->target) { |
231 | 233 | DBG(TAB, ul_debug("tab parse error: [target]")); |
232 | 233 | goto fail; |
233 | 233 | } |
234 | | |
235 | 4.62k | s = skip_separator(s); |
236 | | |
237 | | /* (6) vfs options (fs-independent) */ |
238 | 4.62k | fs->vfs_optstr = unmangle(s, &s); |
239 | 4.62k | if (!fs->vfs_optstr) { |
240 | 218 | DBG(TAB, ul_debug("tab parse error: [VFS options]")); |
241 | 218 | goto fail; |
242 | 218 | } |
243 | | |
244 | | /* (7) optional fields, terminated by " - " */ |
245 | 4.40k | p = strstr(s, " - "); |
246 | 4.40k | if (!p) { |
247 | 218 | DBG(TAB, ul_debug("mountinfo parse error: separator not found")); |
248 | 218 | return -EINVAL; |
249 | 218 | } |
250 | 4.18k | if (p > s + 1) |
251 | 197 | fs->opt_fields = strndup(s + 1, p - s - 1); |
252 | | |
253 | 4.18k | s = skip_separator(p + 3); |
254 | | |
255 | | /* (8) FS type */ |
256 | 4.18k | p = unmangle(s, &s); |
257 | 4.18k | if (!p || (rc = __mnt_fs_set_fstype_ptr(fs, p))) { |
258 | 409 | DBG(TAB, ul_debug("tab parse error: [fstype]")); |
259 | 409 | free(p); |
260 | 409 | goto fail; |
261 | 409 | } |
262 | | |
263 | | /* (9) source -- maybe empty string */ |
264 | 3.77k | if (!s || !*s) { |
265 | 199 | DBG(TAB, ul_debug("tab parse error: [source]")); |
266 | 199 | goto fail; |
267 | 3.58k | } else if (*s == ' ' && *(s+1) == ' ') { |
268 | 1.93k | if ((rc = mnt_fs_set_source(fs, ""))) { |
269 | 0 | DBG(TAB, ul_debug("tab parse error: [empty source]")); |
270 | 0 | goto fail; |
271 | 0 | } |
272 | 1.93k | } else { |
273 | 1.64k | s = skip_separator(s); |
274 | 1.64k | p = unmangle(s, &s); |
275 | 1.64k | if (!p || (rc = __mnt_fs_set_source_ptr(fs, p))) { |
276 | 406 | DBG(TAB, ul_debug("tab parse error: [regular source]")); |
277 | 406 | free(p); |
278 | 406 | goto fail; |
279 | 406 | } |
280 | 1.64k | } |
281 | | |
282 | 3.17k | s = skip_separator(s); |
283 | | |
284 | | /* (10) fs options (fs specific) */ |
285 | 3.17k | fs->fs_optstr = unmangle(s, &s); |
286 | 3.17k | if (!fs->fs_optstr) { |
287 | 418 | DBG(TAB, ul_debug("tab parse error: [FS options]")); |
288 | 418 | goto fail; |
289 | 418 | } |
290 | | |
291 | | /* merge VFS and FS options to one string */ |
292 | 2.75k | fs->optstr = mnt_fs_strdup_options(fs); |
293 | 2.75k | if (!fs->optstr) { |
294 | 0 | rc = -ENOMEM; |
295 | 0 | DBG(TAB, ul_debug("tab parse error: [merge VFS and FS options]")); |
296 | 0 | goto fail; |
297 | 0 | } |
298 | | |
299 | 2.75k | return 0; |
300 | 5.50k | fail: |
301 | 5.50k | if (rc == 0) |
302 | 3.65k | rc = -EINVAL; |
303 | 5.50k | DBG(TAB, ul_debug("tab parse error on: '%s' [rc=%d]", s, rc)); |
304 | 5.50k | return rc; |
305 | 2.75k | } |
306 | | |
307 | | /* |
308 | | * Parses one line from utab file |
309 | | */ |
310 | | static int mnt_parse_utab_line(struct libmnt_fs *fs, const char *s) |
311 | 0 | { |
312 | 0 | const char *p = s; |
313 | |
|
314 | 0 | assert(fs); |
315 | 0 | assert(s); |
316 | 0 | assert(!fs->source); |
317 | 0 | assert(!fs->target); |
318 | | |
319 | 0 | while (p && *p) { |
320 | 0 | const char *end = NULL; |
321 | |
|
322 | 0 | while (*p == ' ') p++; |
323 | 0 | if (!*p) |
324 | 0 | break; |
325 | | |
326 | 0 | if (!fs->id && !strncmp(p, "ID=", 3)) { |
327 | 0 | int rc = 0; |
328 | |
|
329 | 0 | end = next_s32(p + 3, &fs->id, &rc); |
330 | 0 | if (!end || rc) |
331 | 0 | return rc; |
332 | |
|
333 | 0 | } else if (!fs->source && !strncmp(p, "SRC=", 4)) { |
334 | 0 | char *v = unmangle(p + 4, &end); |
335 | 0 | if (!v) |
336 | 0 | goto enomem; |
337 | 0 | if (__mnt_fs_set_source_ptr(fs, v)) |
338 | 0 | free(v); |
339 | |
|
340 | 0 | } else if (!fs->target && !strncmp(p, "TARGET=", 7)) { |
341 | 0 | fs->target = unmangle(p + 7, &end); |
342 | 0 | if (!fs->target) |
343 | 0 | goto enomem; |
344 | |
|
345 | 0 | } else if (!fs->root && !strncmp(p, "ROOT=", 5)) { |
346 | 0 | fs->root = unmangle(p + 5, &end); |
347 | 0 | if (!fs->root) |
348 | 0 | goto enomem; |
349 | |
|
350 | 0 | } else if (!fs->bindsrc && !strncmp(p, "BINDSRC=", 8)) { |
351 | 0 | fs->bindsrc = unmangle(p + 8, &end); |
352 | 0 | if (!fs->bindsrc) |
353 | 0 | goto enomem; |
354 | |
|
355 | 0 | } else if (!fs->user_optstr && !strncmp(p, "OPTS=", 5)) { |
356 | 0 | fs->user_optstr = unmangle(p + 5, &end); |
357 | 0 | if (!fs->user_optstr) |
358 | 0 | goto enomem; |
359 | |
|
360 | 0 | } else if (!fs->attrs && !strncmp(p, "ATTRS=", 6)) { |
361 | 0 | fs->attrs = unmangle(p + 6, &end); |
362 | 0 | if (!fs->attrs) |
363 | 0 | goto enomem; |
364 | |
|
365 | 0 | } else { |
366 | | /* unknown variable */ |
367 | 0 | while (*p && *p != ' ') p++; |
368 | 0 | } |
369 | 0 | if (end) |
370 | 0 | p = end; |
371 | 0 | } |
372 | | |
373 | 0 | return 0; |
374 | 0 | enomem: |
375 | 0 | DBG(TAB, ul_debug("utab parse error: ENOMEM")); |
376 | 0 | return -ENOMEM; |
377 | 0 | } |
378 | | |
379 | | /* |
380 | | * Parses one line from /proc/swaps |
381 | | */ |
382 | | static int mnt_parse_swaps_line(struct libmnt_fs *fs, const char *s) |
383 | 5.12k | { |
384 | 5.12k | uint64_t num; |
385 | 5.12k | int rc = 0; |
386 | 5.12k | char *p; |
387 | | |
388 | | /* (1) source */ |
389 | 5.12k | p = unmangle(s, &s); |
390 | 5.12k | if (p) { |
391 | 5.12k | char *x = (char *) endswith(p, PATH_DELETED_SUFFIX); |
392 | 5.12k | if (x && *x) |
393 | 194 | *x = '\0'; |
394 | 5.12k | } |
395 | 5.12k | if (!p || (rc = __mnt_fs_set_source_ptr(fs, p))) { |
396 | 0 | DBG(TAB, ul_debug("tab parse error: [source]")); |
397 | 0 | free(p); |
398 | 0 | goto fail; |
399 | 0 | } |
400 | | |
401 | 5.12k | s = skip_separator(s); |
402 | | |
403 | | /* (2) type */ |
404 | 5.12k | fs->swaptype = unmangle(s, &s); |
405 | 5.12k | if (!fs->swaptype) { |
406 | 1.24k | DBG(TAB, ul_debug("tab parse error: [swaptype]")); |
407 | 1.24k | goto fail; |
408 | 1.24k | } |
409 | | |
410 | 3.88k | s = skip_separator(s); |
411 | | |
412 | | /* (3) size */ |
413 | 3.88k | s = next_u64(s, &num, &rc); |
414 | 3.88k | if (!s || !*s || rc) { |
415 | 1.62k | DBG(TAB, ul_debug("tab parse error: [size]")); |
416 | 1.62k | goto fail; |
417 | 1.62k | } |
418 | 2.25k | fs->size = num; |
419 | | |
420 | 2.25k | s = skip_separator(s); |
421 | | |
422 | | /* (4) size */ |
423 | 2.25k | s = next_u64(s, &num, &rc); |
424 | 2.25k | if (!s || !*s || rc) { |
425 | 1.30k | DBG(TAB, ul_debug("tab parse error: [used size]")); |
426 | 1.30k | goto fail; |
427 | 1.30k | } |
428 | 952 | fs->usedsize = num; |
429 | | |
430 | 952 | s = skip_separator(s); |
431 | | |
432 | | /* (5) priority */ |
433 | 952 | s = next_s32(s, &fs->priority, &rc); |
434 | 952 | if (rc) { |
435 | 210 | DBG(TAB, ul_debug("tab parse error: [priority]")); |
436 | 210 | goto fail; |
437 | 210 | } |
438 | | |
439 | 742 | mnt_fs_set_fstype(fs, "swap"); |
440 | 742 | return 0; |
441 | 4.38k | fail: |
442 | 4.38k | if (rc == 0) |
443 | 2.54k | rc = -EINVAL; |
444 | 4.38k | DBG(TAB, ul_debug("tab parse error on: '%s' [rc=%d]", s, rc)); |
445 | 4.38k | return rc; |
446 | 952 | } |
447 | | |
448 | | |
449 | | /* |
450 | | * Returns {m,fs}tab or mountinfo file format (MNT_FMT_*) |
451 | | * |
452 | | * Note that we aren't trying to guess the utab file format, because this file |
453 | | * always has to be parsed by private libmount routines with an explicitly defined |
454 | | * format. |
455 | | * |
456 | | * mountinfo: "<number> <number> ... " |
457 | | */ |
458 | | static int guess_table_format(const char *line) |
459 | 3.19k | { |
460 | 3.19k | unsigned int a, b; |
461 | | |
462 | 3.19k | DBG(TAB, ul_debug("trying to guess table type")); |
463 | | |
464 | 3.19k | if (sscanf(line, "%u %u", &a, &b) == 2) |
465 | 919 | return MNT_FMT_MOUNTINFO; |
466 | | |
467 | 2.27k | if (strncmp(line, "Filename\t", 9) == 0) |
468 | 447 | return MNT_FMT_SWAPS; |
469 | | |
470 | 1.83k | return MNT_FMT_FSTAB; /* fstab, or /proc/mounts */ |
471 | 2.27k | } |
472 | | |
473 | | static int is_comment_line(const char *line) |
474 | 59.2k | { |
475 | 59.2k | const char *p = skip_blank(line); |
476 | | |
477 | 59.2k | if (p && (*p == '#' || *p == '\n')) |
478 | 3.99k | return 1; |
479 | 55.2k | return 0; |
480 | 59.2k | } |
481 | | |
482 | | /* returns 1 if the last line in the @str is blank */ |
483 | | static int is_terminated_by_blank(const char *str) |
484 | 2.77k | { |
485 | 2.77k | size_t sz = str ? strlen(str) : 0; |
486 | 2.77k | const char *p = sz ? str + (sz - 1) : NULL; |
487 | | |
488 | 2.77k | if (!sz || !p || *p != '\n') |
489 | 423 | return 0; /* empty or not terminated by '\n' */ |
490 | 2.35k | if (p == str) |
491 | 1.65k | return 1; /* only '\n' */ |
492 | 699 | p--; |
493 | 1.29k | while (p > str && (*p == ' ' || *p == '\t')) |
494 | 600 | p--; |
495 | 699 | return *p == '\n' ? 1 : 0; |
496 | 2.35k | } |
497 | | |
498 | | /* |
499 | | * Reads the next line from the file. |
500 | | * |
501 | | * Returns 0 if the line is a comment |
502 | | * 1 if the line is not a comment |
503 | | * <0 on error |
504 | | */ |
505 | | static int next_comment_line(struct libmnt_parser *pa, char **last) |
506 | 3.99k | { |
507 | 3.99k | if (getline(&pa->buf, &pa->bufsiz, pa->f) < 0) |
508 | 159 | return feof(pa->f) ? 1 : -errno; |
509 | | |
510 | 3.83k | pa->line++; |
511 | 3.83k | *last = strchr(pa->buf, '\n'); |
512 | | |
513 | 3.83k | return is_comment_line(pa->buf) ? 0 : 1; |
514 | 3.99k | } |
515 | | |
516 | | static int append_comment(struct libmnt_table *tb, |
517 | | struct libmnt_fs *fs, |
518 | | const char *comm, |
519 | | int eof) |
520 | 4.20k | { |
521 | 4.20k | int rc, intro = mnt_table_get_nents(tb) == 0; |
522 | | |
523 | 4.20k | if (intro && is_terminated_by_blank(mnt_table_get_intro_comment(tb))) |
524 | 1.90k | intro = 0; |
525 | | |
526 | 4.20k | DBG(TAB, ul_debugobj(tb, "appending %s comment", |
527 | 4.20k | intro ? "intro" : |
528 | 4.20k | eof ? "trailing" : "fs")); |
529 | 4.20k | if (intro) |
530 | 878 | rc = mnt_table_append_intro_comment(tb, comm); |
531 | 3.32k | else if (eof) { |
532 | 149 | rc = mnt_table_set_trailing_comment(tb, |
533 | 149 | mnt_fs_get_comment(fs)); |
534 | 149 | if (!rc) |
535 | 149 | rc = mnt_table_append_trailing_comment(tb, comm); |
536 | 149 | if (!rc) |
537 | 149 | rc = mnt_fs_set_comment(fs, NULL); |
538 | 149 | } else |
539 | 3.17k | rc = mnt_fs_append_comment(fs, comm); |
540 | 4.20k | return rc; |
541 | 4.20k | } |
542 | | |
543 | | /* |
544 | | * Read and parse the next line from {fs,m}tab or mountinfo |
545 | | */ |
546 | | static int mnt_table_parse_next(struct libmnt_parser *pa, |
547 | | struct libmnt_table *tb, |
548 | | struct libmnt_fs *fs) |
549 | 72.6k | { |
550 | 72.6k | char *s; |
551 | 72.6k | int rc; |
552 | | |
553 | 72.6k | assert(tb); |
554 | 72.6k | assert(pa); |
555 | 72.6k | assert(fs); |
556 | | |
557 | | /* read the next non-blank non-comment line */ |
558 | 73.2k | next_line: |
559 | 74.0k | do { |
560 | 74.0k | if (getline(&pa->buf, &pa->bufsiz, pa->f) < 0) |
561 | 202 | return -EINVAL; |
562 | 73.8k | pa->line++; |
563 | 73.8k | s = strchr(pa->buf, '\n'); |
564 | 73.8k | if (!s) { |
565 | 5.99k | DBG(TAB, ul_debugobj(tb, "%s:%zu: no final newline", |
566 | 5.99k | pa->filename, pa->line)); |
567 | | |
568 | | /* Missing final newline? Otherwise an extremely */ |
569 | | /* long line - assume file was corrupted */ |
570 | 5.99k | if (feof(pa->f)) |
571 | 3.12k | s = memchr(pa->buf, '\0', pa->bufsiz); |
572 | | |
573 | | /* comments parser */ |
574 | 67.8k | } else if (tb->comms |
575 | 67.8k | && (tb->fmt == MNT_FMT_GUESS || tb->fmt == MNT_FMT_FSTAB) |
576 | 67.8k | && is_comment_line(pa->buf)) { |
577 | 3.99k | do { |
578 | 3.99k | rc = append_comment(tb, fs, pa->buf, feof(pa->f)); |
579 | 3.99k | if (!rc) |
580 | 3.99k | rc = next_comment_line(pa, &s); |
581 | 3.99k | } while (rc == 0); |
582 | | |
583 | 1.25k | if (rc == 1 && feof(pa->f)) |
584 | 208 | rc = append_comment(tb, fs, NULL, 1); |
585 | 1.25k | if (rc < 0) |
586 | 0 | return rc; |
587 | | |
588 | 1.25k | } |
589 | | |
590 | 73.8k | if (!s) |
591 | 3.18k | goto err; |
592 | 70.6k | *s = '\0'; |
593 | 70.6k | if (s > pa->buf && *(s - 1) == '\r') |
594 | 393 | *(--s) = '\0'; |
595 | 70.6k | s = (char *) skip_blank(pa->buf); |
596 | 70.6k | } while (*s == '\0' || *s == '#'); |
597 | | |
598 | 69.8k | if (tb->fmt == MNT_FMT_GUESS) { |
599 | 3.19k | tb->fmt = guess_table_format(s); |
600 | 3.19k | if (tb->fmt == MNT_FMT_SWAPS) |
601 | 447 | goto next_line; /* skip swap header */ |
602 | 3.19k | } |
603 | | |
604 | 69.4k | switch (tb->fmt) { |
605 | 55.6k | case MNT_FMT_FSTAB: |
606 | 55.6k | rc = mnt_parse_table_line(fs, s); |
607 | 55.6k | break; |
608 | 8.47k | case MNT_FMT_MOUNTINFO: |
609 | 8.47k | rc = mnt_parse_mountinfo_line(fs, s); |
610 | 8.47k | break; |
611 | 0 | case MNT_FMT_UTAB: |
612 | 0 | rc = mnt_parse_utab_line(fs, s); |
613 | 0 | break; |
614 | 5.32k | case MNT_FMT_SWAPS: |
615 | 5.32k | if (strncmp(s, "Filename\t", 9) == 0) |
616 | 196 | goto next_line; /* skip swap header */ |
617 | 5.12k | rc = mnt_parse_swaps_line(fs, s); |
618 | 5.12k | break; |
619 | 0 | default: |
620 | 0 | rc = -1; /* unknown format */ |
621 | 0 | break; |
622 | 69.4k | } |
623 | | |
624 | 69.2k | if (rc == 0) |
625 | 45.9k | return 0; |
626 | 26.4k | err: |
627 | 26.4k | DBG(TAB, ul_debugobj(tb, "%s:%zu: %s parse error", pa->filename, pa->line, |
628 | 26.4k | tb->fmt == MNT_FMT_MOUNTINFO ? "mountinfo" : |
629 | 26.4k | tb->fmt == MNT_FMT_SWAPS ? "swaps" : |
630 | 26.4k | tb->fmt == MNT_FMT_FSTAB ? "tab" : "utab")); |
631 | | |
632 | | /* by default all errors are recoverable, otherwise behavior depends on |
633 | | * the errcb() function. See mnt_table_set_parser_errcb(). |
634 | | */ |
635 | 26.4k | return tb->errcb ? tb->errcb(tb, pa->filename, pa->line) : 1; |
636 | 69.2k | } |
637 | | |
638 | | static pid_t path_to_tid(const char *filename) |
639 | 439 | { |
640 | 439 | char *path = mnt_resolve_path(filename, NULL); |
641 | 439 | char *p, *end = NULL; |
642 | 439 | pid_t tid = 0; |
643 | | |
644 | 439 | if (!path) |
645 | 0 | goto done; |
646 | 439 | p = strrchr(path, '/'); |
647 | 439 | if (!p) |
648 | 439 | goto done; |
649 | 0 | *p = '\0'; |
650 | 0 | p = strrchr(path, '/'); |
651 | 0 | if (!p) |
652 | 0 | goto done; |
653 | 0 | p++; |
654 | |
|
655 | 0 | errno = 0; |
656 | 0 | tid = strtol(p, &end, 10); |
657 | 0 | if (errno || p == end || (end && *end)) { |
658 | 0 | tid = 0; |
659 | 0 | goto done; |
660 | 0 | } |
661 | 0 | DBG(TAB, ul_debug("TID for %s is %d", filename, tid)); |
662 | 439 | done: |
663 | 439 | free(path); |
664 | 439 | return tid; |
665 | 0 | } |
666 | | |
667 | | static int kernel_fs_postparse(struct libmnt_parser *pa, |
668 | | struct libmnt_table *tb, |
669 | | struct libmnt_fs *fs, pid_t *tid) |
670 | 2.75k | { |
671 | 2.75k | int rc = 0; |
672 | 2.75k | const char *src = mnt_fs_get_srcpath(fs); |
673 | | |
674 | | /* This is a filesystem description from /proc, so we're in some process |
675 | | * namespace. Let's remember the process PID. |
676 | | */ |
677 | 2.75k | if (pa->filename && *tid == -1) |
678 | 439 | *tid = path_to_tid(pa->filename); |
679 | | |
680 | 2.75k | fs->tid = *tid; |
681 | | |
682 | | /* |
683 | | * Convert obscure /dev/root to something more usable |
684 | | */ |
685 | 2.75k | if (src && strcmp(src, "/dev/root") == 0) { |
686 | | |
687 | | /* We will only call mnt_guess_system_root() if it has not |
688 | | * been called before. Inside a container, mountinfo can contain |
689 | | * many lines with /dev/root. |
690 | | */ |
691 | 715 | if (pa->sysroot_rc == 0 && pa->sysroot == NULL) |
692 | 48 | pa->sysroot_rc = mnt_guess_system_root(fs->devno, |
693 | 48 | tb->cache, &pa->sysroot); |
694 | | |
695 | 715 | rc = pa->sysroot_rc; |
696 | 715 | if (rc < 0) |
697 | 0 | return rc; |
698 | | |
699 | | /* This means that we already have run the mnt_guess_system_root() |
700 | | * and that we want to reuse the result. |
701 | | */ |
702 | 715 | if (rc == 0 && pa->sysroot != NULL) { |
703 | 715 | char *real = strdup(pa->sysroot); |
704 | | |
705 | 715 | if (!real) |
706 | 0 | return -ENOMEM; |
707 | | |
708 | 715 | DBG(TAB, ul_debugobj(tb, "canonical root FS: %s", real)); |
709 | 715 | rc = __mnt_fs_set_source_ptr(fs, real); |
710 | | |
711 | 715 | } else if (rc == 1) { |
712 | | /* mnt_guess_system_root() returns 1 if not able to convert to |
713 | | * the real devname; ignore this problem */ |
714 | 0 | rc = 0; |
715 | 0 | } |
716 | 715 | } |
717 | | |
718 | 2.75k | return rc; |
719 | 2.75k | } |
720 | | |
721 | | /** |
722 | | * mnt_table_parse_stream: |
723 | | * @tb: tab pointer |
724 | | * @f: file stream |
725 | | * @filename: filename used for debug and error messages |
726 | | * |
727 | | * Returns: 0 on success, negative number in case of error. |
728 | | */ |
729 | | int mnt_table_parse_stream(struct libmnt_table *tb, FILE *f, const char *filename) |
730 | 3.39k | { |
731 | 3.39k | int rc = -1; |
732 | 3.39k | int flags = 0; |
733 | 3.39k | pid_t tid = -1; |
734 | 3.39k | struct libmnt_parser pa = { .line = 0 }; |
735 | | |
736 | 3.39k | assert(tb); |
737 | 3.39k | assert(f); |
738 | 3.39k | assert(filename); |
739 | | |
740 | 3.39k | DBG(TAB, ul_debugobj(tb, "%s: start parsing [entries=%d, filter=%s]", |
741 | 3.39k | filename, mnt_table_get_nents(tb), |
742 | 3.39k | tb->fltrcb ? "yes" : "not")); |
743 | | |
744 | 3.39k | pa.filename = filename; |
745 | 3.39k | pa.f = f; |
746 | | |
747 | | /* necessary for /proc/mounts only, the /proc/self/mountinfo |
748 | | * parser sets the flag properly |
749 | | */ |
750 | 3.39k | if (tb->fmt == MNT_FMT_SWAPS) |
751 | 0 | flags = MNT_FS_SWAP; |
752 | 3.39k | else if (filename && strcmp(filename, _PATH_PROC_MOUNTS) == 0) |
753 | 0 | flags = MNT_FS_KERNEL; |
754 | | |
755 | 76.0k | do { |
756 | 76.0k | struct libmnt_fs *fs; |
757 | | |
758 | 76.0k | if (feof(f)) { |
759 | 3.39k | DBG(TAB, ul_debugobj(tb, "end-of-file")); |
760 | 3.39k | break; |
761 | 3.39k | } |
762 | 72.6k | fs = mnt_new_fs(); |
763 | 72.6k | if (!fs) |
764 | 0 | goto err; |
765 | | |
766 | | /* parse */ |
767 | 72.6k | rc = mnt_table_parse_next(&pa, tb, fs); |
768 | | |
769 | 72.6k | if (rc == 0 && tb->fltrcb && tb->fltrcb(fs, tb->fltrcb_data)) |
770 | 0 | rc = 1; /* filtered out by callback... */ |
771 | | |
772 | 72.6k | if (rc == 0 && mnt_table_is_noautofs(tb)) { |
773 | 0 | const char *fstype = mnt_fs_get_fstype(fs); |
774 | |
|
775 | 0 | if (fstype && strcmp(fstype, "autofs") == 0 && |
776 | 0 | mnt_fs_get_option(fs, "ignore", NULL, NULL) == 0) |
777 | 0 | rc = 1; /* Skip "ignore" autofs entry */ |
778 | 0 | } |
779 | | |
780 | | /* add to the table */ |
781 | 72.6k | if (rc == 0) { |
782 | 45.9k | rc = mnt_table_add_fs(tb, fs); |
783 | 45.9k | fs->flags |= flags; |
784 | | |
785 | 45.9k | if (rc == 0 && tb->fmt == MNT_FMT_MOUNTINFO) { |
786 | 2.75k | rc = kernel_fs_postparse(&pa, tb, fs, &tid); |
787 | 2.75k | if (rc) |
788 | 0 | mnt_table_remove_fs(tb, fs); |
789 | 2.75k | } |
790 | 45.9k | } |
791 | | |
792 | | /* remove reference (or deallocate on error) */ |
793 | 72.6k | mnt_unref_fs(fs); |
794 | | |
795 | | /* recoverable error */ |
796 | 72.6k | if (rc > 0) { |
797 | 26.4k | DBG(TAB, ul_debugobj(tb, "recoverable error (continue)")); |
798 | 26.4k | continue; |
799 | 26.4k | } |
800 | | |
801 | | /* fatal errors */ |
802 | 46.1k | if (rc < 0 && !feof(f)) { |
803 | 0 | DBG(TAB, ul_debugobj(tb, "fatal error")); |
804 | 0 | goto err; |
805 | 0 | } |
806 | 72.6k | } while (1); |
807 | | |
808 | 3.39k | DBG(TAB, ul_debugobj(tb, "%s: stop parsing (%d entries)", |
809 | 3.39k | filename, mnt_table_get_nents(tb))); |
810 | 3.39k | parser_cleanup(&pa); |
811 | 3.39k | return 0; |
812 | 0 | err: |
813 | 0 | DBG(TAB, ul_debugobj(tb, "%s: parse error (rc=%d)", filename, rc)); |
814 | 0 | parser_cleanup(&pa); |
815 | 0 | return rc; |
816 | 3.39k | } |
817 | | |
818 | | /** |
819 | | * mnt_table_parse_file: |
820 | | * @tb: tab pointer |
821 | | * @filename: file |
822 | | * |
823 | | * Parses the whole table (e.g. /etc/fstab) and appends new records to the @tab. |
824 | | * |
825 | | * The libmount parser ignores broken (syntax error) lines, these lines are |
826 | | * reported to the caller by the errcb() function (see mnt_table_set_parser_errcb()). |
827 | | * |
828 | | * Returns: 0 on success, negative number in case of error. |
829 | | */ |
830 | | int mnt_table_parse_file(struct libmnt_table *tb, const char *filename) |
831 | 0 | { |
832 | 0 | FILE *f; |
833 | 0 | int rc; |
834 | |
|
835 | 0 | if (!filename || !tb) |
836 | 0 | return -EINVAL; |
837 | | |
838 | 0 | f = fopen(filename, "r" UL_CLOEXECSTR); |
839 | 0 | if (f) { |
840 | 0 | rc = mnt_table_parse_stream(tb, f, filename); |
841 | 0 | fclose(f); |
842 | 0 | } else |
843 | 0 | rc = -errno; |
844 | |
|
845 | 0 | DBG(TAB, ul_debugobj(tb, "parsing done [filename=%s, rc=%d]", filename, rc)); |
846 | 0 | return rc; |
847 | 0 | } |
848 | | |
849 | | static int mnt_table_parse_dir_filter(const struct dirent *d) |
850 | 0 | { |
851 | 0 | size_t namesz; |
852 | |
|
853 | 0 | #ifdef _DIRENT_HAVE_D_TYPE |
854 | 0 | if (d->d_type != DT_UNKNOWN && d->d_type != DT_REG && |
855 | 0 | d->d_type != DT_LNK) |
856 | 0 | return 0; |
857 | 0 | #endif |
858 | 0 | if (*d->d_name == '.') |
859 | 0 | return 0; |
860 | | |
861 | 0 | #define MNT_MNTTABDIR_EXTSIZ (sizeof(MNT_MNTTABDIR_EXT) - 1) |
862 | | |
863 | 0 | namesz = strlen(d->d_name); |
864 | 0 | if (!namesz || namesz < MNT_MNTTABDIR_EXTSIZ + 1 || |
865 | 0 | strcmp(d->d_name + (namesz - MNT_MNTTABDIR_EXTSIZ), |
866 | 0 | MNT_MNTTABDIR_EXT) != 0) |
867 | 0 | return 0; |
868 | | |
869 | | /* Accept this */ |
870 | 0 | return 1; |
871 | 0 | } |
872 | | |
873 | | #ifdef HAVE_SCANDIRAT |
874 | | static int __mnt_table_parse_dir(struct libmnt_table *tb, const char *dirname) |
875 | 0 | { |
876 | 0 | int n = 0, i; |
877 | 0 | int dd; |
878 | 0 | struct dirent **namelist = NULL; |
879 | |
|
880 | 0 | dd = open(dirname, O_RDONLY|O_CLOEXEC|O_DIRECTORY); |
881 | 0 | if (dd < 0) |
882 | 0 | return -errno; |
883 | | |
884 | 0 | n = scandirat(dd, ".", &namelist, mnt_table_parse_dir_filter, versionsort); |
885 | 0 | if (n <= 0) { |
886 | 0 | close(dd); |
887 | 0 | return 0; |
888 | 0 | } |
889 | | |
890 | 0 | for (i = 0; i < n; i++) { |
891 | 0 | struct dirent *d = namelist[i]; |
892 | 0 | struct stat st; |
893 | 0 | FILE *f; |
894 | |
|
895 | 0 | if (fstatat(dd, d->d_name, &st, 0) || |
896 | 0 | !S_ISREG(st.st_mode)) |
897 | 0 | continue; |
898 | | |
899 | 0 | f = fopen_at(dd, d->d_name, O_RDONLY|O_CLOEXEC, "r" UL_CLOEXECSTR); |
900 | 0 | if (f) { |
901 | 0 | mnt_table_parse_stream(tb, f, d->d_name); |
902 | 0 | fclose(f); |
903 | 0 | } |
904 | 0 | } |
905 | |
|
906 | 0 | for (i = 0; i < n; i++) |
907 | 0 | free(namelist[i]); |
908 | 0 | free(namelist); |
909 | 0 | close(dd); |
910 | 0 | return 0; |
911 | 0 | } |
912 | | #else |
913 | | static int __mnt_table_parse_dir(struct libmnt_table *tb, const char *dirname) |
914 | | { |
915 | | int n = 0, i, r = 0; |
916 | | DIR *dir = NULL; |
917 | | struct dirent **namelist = NULL; |
918 | | |
919 | | n = scandir(dirname, &namelist, mnt_table_parse_dir_filter, versionsort); |
920 | | if (n <= 0) |
921 | | return 0; |
922 | | |
923 | | /* let's use "at" functions rather than playing crazy games with paths... */ |
924 | | dir = opendir(dirname); |
925 | | if (!dir) { |
926 | | r = -errno; |
927 | | goto out; |
928 | | } |
929 | | |
930 | | for (i = 0; i < n; i++) { |
931 | | struct dirent *d = namelist[i]; |
932 | | struct stat st; |
933 | | FILE *f; |
934 | | |
935 | | if (fstatat(dirfd(dir), d->d_name, &st, 0) || |
936 | | !S_ISREG(st.st_mode)) |
937 | | continue; |
938 | | |
939 | | f = fopen_at(dirfd(dir), d->d_name, |
940 | | O_RDONLY|O_CLOEXEC, "r" UL_CLOEXECSTR); |
941 | | if (f) { |
942 | | mnt_table_parse_stream(tb, f, d->d_name); |
943 | | fclose(f); |
944 | | } |
945 | | } |
946 | | |
947 | | out: |
948 | | for (i = 0; i < n; i++) |
949 | | free(namelist[i]); |
950 | | free(namelist); |
951 | | if (dir) |
952 | | closedir(dir); |
953 | | return r; |
954 | | } |
955 | | #endif |
956 | | |
957 | | /** |
958 | | * mnt_table_parse_dir: |
959 | | * @tb: mount table |
960 | | * @dirname: directory |
961 | | * |
962 | | * The directory: |
963 | | * - files are sorted by strverscmp(3) |
964 | | * - files that start with "." are ignored (e.g. ".10foo.fstab") |
965 | | * - files without the ".fstab" extension are ignored |
966 | | * |
967 | | * Returns: 0 on success or negative number in case of error. |
968 | | */ |
969 | | int mnt_table_parse_dir(struct libmnt_table *tb, const char *dirname) |
970 | 0 | { |
971 | 0 | return __mnt_table_parse_dir(tb, dirname); |
972 | 0 | } |
973 | | |
974 | | struct libmnt_table *__mnt_new_table_from_file(const char *filename, int fmt, int empty_for_enoent) |
975 | 0 | { |
976 | 0 | struct libmnt_table *tb; |
977 | |
|
978 | 0 | if (!filename) |
979 | 0 | return NULL; |
980 | 0 | if (!mnt_is_path(filename)) |
981 | 0 | return empty_for_enoent ? mnt_new_table() : NULL; |
982 | | |
983 | 0 | tb = mnt_new_table(); |
984 | 0 | if (tb) { |
985 | 0 | DBG(TAB, ul_debugobj(tb, "new tab for file: %s", filename)); |
986 | 0 | tb->fmt = fmt; |
987 | 0 | if (mnt_table_parse_file(tb, filename) != 0) { |
988 | 0 | mnt_unref_table(tb); |
989 | 0 | tb = NULL; |
990 | 0 | } |
991 | 0 | } |
992 | 0 | return tb; |
993 | 0 | } |
994 | | |
995 | | /** |
996 | | * mnt_new_table_from_file: |
997 | | * @filename: /etc/{m,fs}tab or /proc/self/mountinfo path |
998 | | * |
999 | | * Same as mnt_new_table() + mnt_table_parse_file(). Use this function for private |
1000 | | * files only. This function does not allow using the error callback, so you |
1001 | | * cannot provide any feedback to end-users about broken records in files (e.g. |
1002 | | * fstab). |
1003 | | * |
1004 | | * Returns: newly allocated tab on success and NULL in case of error. |
1005 | | */ |
1006 | | struct libmnt_table *mnt_new_table_from_file(const char *filename) |
1007 | 0 | { |
1008 | 0 | if (!filename) |
1009 | 0 | return NULL; |
1010 | | |
1011 | 0 | return __mnt_new_table_from_file(filename, MNT_FMT_GUESS, 0); |
1012 | 0 | } |
1013 | | |
1014 | | /** |
1015 | | * mnt_new_table_from_dir |
1016 | | * @dirname: directory with *.fstab files |
1017 | | * |
1018 | | * Returns: newly allocated tab on success and NULL in case of error. |
1019 | | */ |
1020 | | struct libmnt_table *mnt_new_table_from_dir(const char *dirname) |
1021 | 0 | { |
1022 | 0 | struct libmnt_table *tb; |
1023 | |
|
1024 | 0 | if (!dirname) |
1025 | 0 | return NULL; |
1026 | 0 | tb = mnt_new_table(); |
1027 | 0 | if (tb && mnt_table_parse_dir(tb, dirname) != 0) { |
1028 | 0 | mnt_unref_table(tb); |
1029 | 0 | tb = NULL; |
1030 | 0 | } |
1031 | 0 | return tb; |
1032 | 0 | } |
1033 | | |
1034 | | /** |
1035 | | * mnt_table_set_parser_errcb: |
1036 | | * @tb: pointer to table |
1037 | | * @cb: pointer to callback function |
1038 | | * |
1039 | | * The error callback function is called by table parser (mnt_table_parse_file()) |
1040 | | * in case of a syntax error. The callback function could be used for error |
1041 | | * evaluation, libmount will continue/stop parsing according to callback return |
1042 | | * codes: |
1043 | | * |
1044 | | * <0 : fatal error (abort parsing) |
1045 | | * 0 : success (parsing continues) |
1046 | | * >0 : recoverable error (the line is ignored, parsing continues). |
1047 | | * |
1048 | | * Returns: 0 on success or negative number in case of error. |
1049 | | */ |
1050 | | int mnt_table_set_parser_errcb(struct libmnt_table *tb, |
1051 | | int (*cb)(struct libmnt_table *tb, const char *filename, int line)) |
1052 | 0 | { |
1053 | 0 | if (!tb) |
1054 | 0 | return -EINVAL; |
1055 | 0 | tb->errcb = cb; |
1056 | 0 | return 0; |
1057 | 0 | } |
1058 | | |
1059 | | /* |
1060 | | * Filter out entries during tab file parsing. If @cb returns 1, then the entry |
1061 | | * is ignored. |
1062 | | */ |
1063 | | int mnt_table_set_parser_fltrcb(struct libmnt_table *tb, |
1064 | | int (*cb)(struct libmnt_fs *, void *), |
1065 | | void *data) |
1066 | 0 | { |
1067 | 0 | if (!tb) |
1068 | 0 | return -EINVAL; |
1069 | | |
1070 | 0 | DBG(TAB, ul_debugobj(tb, "%s table parser filter", cb ? "set" : "unset")); |
1071 | 0 | tb->fltrcb = cb; |
1072 | 0 | tb->fltrcb_data = data; |
1073 | 0 | return 0; |
1074 | 0 | } |
1075 | | |
1076 | | /* |
1077 | | * mnt_table_enable_noautofs: |
1078 | | * @tb: table |
1079 | | * @ignore: ignore or don't ignore |
1080 | | * |
1081 | | * Enable/disable ignore autofs mount table entries on reading. |
1082 | | */ |
1083 | | int mnt_table_enable_noautofs(struct libmnt_table *tb, int ignore) |
1084 | 0 | { |
1085 | 0 | if (!tb) |
1086 | 0 | return -EINVAL; |
1087 | 0 | tb->noautofs = ignore ? 1 : 0; |
1088 | 0 | return 0; |
1089 | 0 | } |
1090 | | |
1091 | | /* |
1092 | | * mnt_table_is_noautofs: |
1093 | | * @tb: table |
1094 | | * |
1095 | | * Return the the enabled status of ignore autofs mount table entries. |
1096 | | */ |
1097 | | int mnt_table_is_noautofs(struct libmnt_table *tb) |
1098 | 45.9k | { |
1099 | 45.9k | return tb ? tb->noautofs : 0; |
1100 | 45.9k | } |
1101 | | |
1102 | | /** |
1103 | | * mnt_table_parse_swaps: |
1104 | | * @tb: table |
1105 | | * @filename: overwrites default (/proc/swaps or $LIBMOUNT_SWAPS) or NULL |
1106 | | * |
1107 | | * This function parses /proc/swaps and appends new lines to the @tab. |
1108 | | * |
1109 | | * See also mnt_table_set_parser_errcb(). |
1110 | | * |
1111 | | * Returns: 0 on success or negative number in case of error. |
1112 | | */ |
1113 | | int mnt_table_parse_swaps(struct libmnt_table *tb, const char *filename) |
1114 | 0 | { |
1115 | 0 | if (!tb) |
1116 | 0 | return -EINVAL; |
1117 | 0 | if (!filename) { |
1118 | 0 | filename = mnt_get_swaps_path(); |
1119 | 0 | if (!filename) |
1120 | 0 | return -EINVAL; |
1121 | 0 | } |
1122 | | |
1123 | 0 | tb->fmt = MNT_FMT_SWAPS; |
1124 | |
|
1125 | 0 | return mnt_table_parse_file(tb, filename); |
1126 | 0 | } |
1127 | | |
1128 | | /** |
1129 | | * mnt_table_parse_fstab: |
1130 | | * @tb: table |
1131 | | * @filename: overwrites default (/etc/fstab or $LIBMOUNT_FSTAB) or NULL |
1132 | | * |
1133 | | * This function parses /etc/fstab and appends new lines to the @tab. If the |
1134 | | * @filename is a directory, then mnt_table_parse_dir() is called. |
1135 | | * |
1136 | | * See also mnt_table_set_parser_errcb(). |
1137 | | * |
1138 | | * Returns: 0 on success or negative number in case of error. |
1139 | | */ |
1140 | | int mnt_table_parse_fstab(struct libmnt_table *tb, const char *filename) |
1141 | 0 | { |
1142 | 0 | struct stat st; |
1143 | 0 | int rc = 0; |
1144 | |
|
1145 | 0 | if (!tb) |
1146 | 0 | return -EINVAL; |
1147 | 0 | if (!filename) |
1148 | 0 | filename = mnt_get_fstab_path(); |
1149 | 0 | if (!filename) |
1150 | 0 | return -EINVAL; |
1151 | 0 | if (mnt_safe_stat(filename, &st) != 0) |
1152 | 0 | return -errno; |
1153 | | |
1154 | 0 | tb->fmt = MNT_FMT_FSTAB; |
1155 | |
|
1156 | 0 | if (S_ISREG(st.st_mode)) |
1157 | 0 | rc = mnt_table_parse_file(tb, filename); |
1158 | 0 | else if (S_ISDIR(st.st_mode)) |
1159 | 0 | rc = mnt_table_parse_dir(tb, filename); |
1160 | 0 | else |
1161 | 0 | rc = -EINVAL; |
1162 | |
|
1163 | 0 | return rc; |
1164 | 0 | } |
1165 | | |
1166 | | /* |
1167 | | * This function uses @uf to find a corresponding record in @tb, then the record |
1168 | | * from @tb is updated (user specific mount options are added). |
1169 | | * |
1170 | | * Note that @uf must contain only user specific mount options instead of |
1171 | | * VFS options (note that FS options are ignored). |
1172 | | * |
1173 | | * Returns modified filesystem (from @tb) or NULL. |
1174 | | */ |
1175 | | static struct libmnt_fs *mnt_table_merge_user_fs(struct libmnt_table *tb, struct libmnt_fs *uf) |
1176 | 0 | { |
1177 | 0 | struct libmnt_fs *fs; |
1178 | 0 | struct libmnt_iter itr; |
1179 | 0 | const char *optstr, *src, *target, *root, *attrs; |
1180 | 0 | int id; |
1181 | |
|
1182 | 0 | if (!tb || !uf) |
1183 | 0 | return NULL; |
1184 | | |
1185 | 0 | DBG(TAB, ul_debugobj(tb, "merging user fs")); |
1186 | |
|
1187 | 0 | src = mnt_fs_get_srcpath(uf); |
1188 | 0 | target = mnt_fs_get_target(uf); |
1189 | 0 | optstr = mnt_fs_get_user_options(uf); |
1190 | 0 | attrs = mnt_fs_get_attributes(uf); |
1191 | 0 | root = mnt_fs_get_root(uf); |
1192 | 0 | id = mnt_fs_get_id(uf); |
1193 | |
|
1194 | 0 | if (!src || !target || !root || (!attrs && !optstr)) |
1195 | 0 | return NULL; |
1196 | | |
1197 | 0 | mnt_reset_iter(&itr, MNT_ITER_BACKWARD); |
1198 | |
|
1199 | 0 | while(mnt_table_next_fs(tb, &itr, &fs) == 0) { |
1200 | 0 | const char *r = mnt_fs_get_root(fs); |
1201 | |
|
1202 | 0 | if (fs->flags & MNT_FS_MERGED) |
1203 | 0 | continue; |
1204 | | |
1205 | 0 | if (id > 0 && mnt_fs_get_id(fs)) { |
1206 | 0 | DBG(TAB, ul_debugobj(tb, " using ID")); |
1207 | 0 | if (mnt_fs_get_id(fs) == id) |
1208 | 0 | break; |
1209 | 0 | } else if (r && strcmp(r, root) == 0 |
1210 | 0 | && mnt_fs_streq_target(fs, target) |
1211 | 0 | && mnt_fs_streq_srcpath(fs, src)) |
1212 | 0 | break; |
1213 | 0 | } |
1214 | |
|
1215 | 0 | if (fs) { |
1216 | 0 | DBG(TAB, ul_debugobj(tb, " found")); |
1217 | 0 | mnt_fs_append_options(fs, optstr); |
1218 | 0 | mnt_fs_append_attributes(fs, attrs); |
1219 | 0 | mnt_fs_set_bindsrc(fs, mnt_fs_get_bindsrc(uf)); |
1220 | 0 | fs->flags |= MNT_FS_MERGED; |
1221 | |
|
1222 | 0 | DBG(TAB, mnt_fs_print_debug(fs, stderr)); |
1223 | 0 | } |
1224 | 0 | return fs; |
1225 | 0 | } |
1226 | | |
1227 | | /* default filename is /proc/self/mountinfo |
1228 | | */ |
1229 | | int __mnt_table_parse_mountinfo(struct libmnt_table *tb, const char *filename, |
1230 | | struct libmnt_table *u_tb) |
1231 | 0 | { |
1232 | 0 | int rc = 0, priv_utab = 0; |
1233 | 0 | int explicit_file = filename ? 1 : 0; |
1234 | |
|
1235 | 0 | assert(tb); |
1236 | | |
1237 | 0 | if (filename) |
1238 | 0 | DBG(TAB, ul_debugobj(tb, "%s requested as mount table", filename)); |
1239 | |
|
1240 | 0 | if (!filename || strcmp(filename, _PATH_PROC_MOUNTINFO) == 0) { |
1241 | 0 | filename = _PATH_PROC_MOUNTINFO; |
1242 | 0 | tb->fmt = MNT_FMT_MOUNTINFO; |
1243 | 0 | DBG(TAB, ul_debugobj(tb, "mountinfo parse: #1 read mountinfo")); |
1244 | 0 | } else |
1245 | 0 | tb->fmt = MNT_FMT_GUESS; |
1246 | |
|
1247 | 0 | rc = mnt_table_parse_file(tb, filename); |
1248 | 0 | if (rc) { |
1249 | 0 | if (explicit_file) |
1250 | 0 | return rc; |
1251 | | |
1252 | | /* hmm, old kernel? ...try /proc/mounts */ |
1253 | 0 | tb->fmt = MNT_FMT_MTAB; |
1254 | 0 | return mnt_table_parse_file(tb, _PATH_PROC_MOUNTS); |
1255 | 0 | } |
1256 | | |
1257 | 0 | if (!is_mountinfo(tb)) |
1258 | 0 | return 0; |
1259 | 0 | DBG(TAB, ul_debugobj(tb, "mountinfo parse: #2 read utab")); |
1260 | |
|
1261 | 0 | if (mnt_table_get_nents(tb) == 0) |
1262 | 0 | return 0; /* empty, ignore utab */ |
1263 | | /* |
1264 | | * try to read the user specific information from /run/mount/utabs |
1265 | | */ |
1266 | 0 | if (!u_tb) { |
1267 | 0 | const char *utab = mnt_get_utab_path(); |
1268 | |
|
1269 | 0 | if (!utab || is_file_empty(utab)) |
1270 | 0 | return 0; |
1271 | | |
1272 | 0 | u_tb = mnt_new_table(); |
1273 | 0 | if (!u_tb) |
1274 | 0 | return -ENOMEM; |
1275 | | |
1276 | 0 | u_tb->fmt = MNT_FMT_UTAB; |
1277 | 0 | mnt_table_set_parser_fltrcb(u_tb, tb->fltrcb, tb->fltrcb_data); |
1278 | |
|
1279 | 0 | rc = mnt_table_parse_file(u_tb, utab); |
1280 | 0 | priv_utab = 1; |
1281 | 0 | } |
1282 | | |
1283 | 0 | DBG(TAB, ul_debugobj(tb, "mountinfo parse: #3 merge utab")); |
1284 | |
|
1285 | 0 | if (rc == 0) { |
1286 | 0 | struct libmnt_fs *u_fs; |
1287 | 0 | struct libmnt_iter itr; |
1288 | |
|
1289 | 0 | mnt_reset_iter(&itr, MNT_ITER_BACKWARD); |
1290 | | |
1291 | | /* merge user options into mountinfo from the kernel */ |
1292 | 0 | while(mnt_table_next_fs(u_tb, &itr, &u_fs) == 0) |
1293 | 0 | mnt_table_merge_user_fs(tb, u_fs); |
1294 | 0 | } |
1295 | | |
1296 | |
|
1297 | 0 | if (priv_utab) |
1298 | 0 | mnt_unref_table(u_tb); |
1299 | 0 | return 0; |
1300 | 0 | } |
1301 | | /** |
1302 | | * mnt_table_parse_mtab: |
1303 | | * @tb: table |
1304 | | * @filename: overwrites default or NULL |
1305 | | * |
1306 | | * The default filename is /proc/self/mountinfo. If the mount table is a |
1307 | | * mountinfo file then /run/mount/utabs is parsed too and both files are merged |
1308 | | * to the one libmnt_table. |
1309 | | * |
1310 | | * The file /etc/mtab is no more used. The function uses "mtab" in the name for |
1311 | | * backward compatibility only. |
1312 | | * |
1313 | | * It's strongly recommended to use NULL as a @filename to keep code portable. |
1314 | | * |
1315 | | * See also mnt_table_set_parser_errcb(). |
1316 | | * |
1317 | | * Returns: 0 on success or negative number in case of error. |
1318 | | */ |
1319 | | int mnt_table_parse_mtab(struct libmnt_table *tb, const char *filename) |
1320 | 0 | { |
1321 | 0 | return __mnt_table_parse_mountinfo(tb, filename, NULL); |
1322 | 0 | } |