tizen 2.4 release
[external/systemd.git] / src / fstab-generator / fstab-generator.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2012 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   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 License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdio.h>
23 #include <mntent.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <unistd.h>
27
28 #include "log.h"
29 #include "util.h"
30 #include "unit-name.h"
31 #include "path-util.h"
32 #include "mount-setup.h"
33 #include "special.h"
34 #include "mkdir.h"
35 #include "fileio.h"
36 #include "generator.h"
37 #include "strv.h"
38 #include "virt.h"
39
40 static const char *arg_dest = "/tmp";
41 static bool arg_fstab_enabled = true;
42 static char *arg_root_what = NULL;
43 static char *arg_root_fstype = NULL;
44 static char *arg_root_options = NULL;
45 static int arg_root_rw = -1;
46
47
48 #ifdef CONFIG_TIZEN_WIP
49 #define COMMENT_HASFS  "comment=havefs-"
50
51 static bool hasfs_comment(
52                 const char *mntopts,
53                 char **comment_start,
54                 char **comment_end,
55                 char **opt_start,
56                 char **fs_new) {
57         char *comment, *fs, *opt, *end;
58
59         /* comment=havefs-foo-opt=1 */
60
61         comment = strstr(mntopts, COMMENT_HASFS);
62         if (!comment)
63                 return false;
64
65         if (comment_start)
66                 *comment_start = comment;
67
68         end = index(comment, ',');
69
70         if (comment_end)
71                 *comment_end = end ? end + 1 : comment + strlen(comment);
72
73         fs = comment + strlen(COMMENT_HASFS);
74
75         opt = index(fs, '-');
76         if (!opt)
77                 goto err_nofs;
78
79         opt += 1;
80
81         if (opt_start)
82                 *opt_start = opt;
83
84         if (fs_new)
85                 *fs_new = strndup(fs, opt - fs - 1);
86
87         return true;
88
89 err_nofs:
90         *fs_new = NULL;
91         *opt_start = NULL;
92
93         return true;
94 }
95 #endif
96
97 static int mount_find_pri(struct mntent *me, int *ret) {
98         char *end, *pri;
99         unsigned long r;
100
101         assert(me);
102         assert(ret);
103
104         pri = hasmntopt(me, "pri");
105         if (!pri)
106                 return 0;
107
108         pri += 4;
109
110         errno = 0;
111         r = strtoul(pri, &end, 10);
112         if (errno > 0)
113                 return -errno;
114
115         if (end == pri || (*end != ',' && *end != 0))
116                 return -EINVAL;
117
118         *ret = (int) r;
119         return 1;
120 }
121
122 static int add_swap(const char *what, struct mntent *me) {
123         _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
124         _cleanup_fclose_ FILE *f = NULL;
125         bool noauto;
126         int r, pri = -1;
127
128         assert(what);
129         assert(me);
130
131         if (detect_container(NULL) > 0) {
132                 log_info("Running in a container, ignoring fstab swap entry for %s.", what);
133                 return 0;
134         }
135
136         r = mount_find_pri(me, &pri);
137         if (r < 0) {
138                 log_error("Failed to parse priority");
139                 return pri;
140         }
141
142         noauto = !!hasmntopt(me, "noauto");
143
144         name = unit_name_from_path(what, ".swap");
145         if (!name)
146                 return log_oom();
147
148         unit = strjoin(arg_dest, "/", name, NULL);
149         if (!unit)
150                 return log_oom();
151
152         f = fopen(unit, "wxe");
153         if (!f) {
154                 if (errno == EEXIST)
155                         log_error("Failed to create swap unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
156                 else
157                         log_error("Failed to create unit file %s: %m", unit);
158                 return -errno;
159         }
160
161         fprintf(f,
162                 "# Automatically generated by systemd-fstab-generator\n\n"
163                 "[Unit]\n"
164                 "SourcePath=/etc/fstab\n"
165                 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n\n"
166                 "[Swap]\n"
167                 "What=%s\n",
168                 what);
169
170         if (pri >= 0)
171                 fprintf(f,
172                         "Priority=%i\n",
173                         pri);
174
175         fflush(f);
176         if (ferror(f)) {
177                 log_error("Failed to write unit file %s: %m", unit);
178                 return -errno;
179         }
180
181         /* use what as where, to have a nicer error message */
182         r = generator_write_timeouts(arg_dest, what, what, me->mnt_opts, NULL);
183         if (r < 0)
184                 return r;
185
186         if (!noauto) {
187                 lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
188                 if (!lnk)
189                         return log_oom();
190
191                 mkdir_parents_label(lnk, 0755);
192                 if (symlink(unit, lnk) < 0) {
193                         log_error("Failed to create symlink %s: %m", lnk);
194                         return -errno;
195                 }
196         }
197
198         return 0;
199 }
200
201 static bool mount_is_network(struct mntent *me) {
202         assert(me);
203
204         return
205                 hasmntopt(me, "_netdev") ||
206                 fstype_is_network(me->mnt_type);
207 }
208
209 static bool mount_in_initrd(struct mntent *me) {
210         assert(me);
211
212         return
213                 hasmntopt(me, "x-initrd.mount") ||
214                 streq(me->mnt_dir, "/usr");
215 }
216
217 static int add_mount(
218                 const char *what,
219                 const char *where,
220                 const char *fstype,
221                 const char *opts,
222                 int passno,
223                 bool noauto,
224                 bool nofail,
225                 bool automount,
226                 const char *post,
227                 const char *source) {
228
229         _cleanup_free_ char
230                 *name = NULL, *unit = NULL, *lnk = NULL,
231                 *automount_name = NULL, *automount_unit = NULL,
232                 *filtered = NULL;
233         _cleanup_fclose_ FILE *f = NULL;
234         int r;
235
236         assert(what);
237         assert(where);
238         assert(opts);
239         assert(source);
240
241         if (streq_ptr(fstype, "autofs"))
242                 return 0;
243
244         if (!is_path(where)) {
245                 log_warning("Mount point %s is not a valid path, ignoring.", where);
246                 return 0;
247         }
248
249         if (mount_point_is_api(where) ||
250             mount_point_ignore(where))
251                 return 0;
252
253         if (path_equal(where, "/")) {
254                 /* The root disk is not an option */
255                 automount = false;
256                 noauto = false;
257                 nofail = false;
258         }
259
260         name = unit_name_from_path(where, ".mount");
261         if (!name)
262                 return log_oom();
263
264         unit = strjoin(arg_dest, "/", name, NULL);
265         if (!unit)
266                 return log_oom();
267
268         f = fopen(unit, "wxe");
269         if (!f) {
270                 if (errno == EEXIST)
271                         log_error("Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
272                 else
273                         log_error("Failed to create unit file %s: %m", unit);
274                 return -errno;
275         }
276
277         fprintf(f,
278                 "# Automatically generated by systemd-fstab-generator\n\n"
279                 "[Unit]\n"
280                 "SourcePath=%s\n"
281                 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
282                 source);
283
284         if (post && !noauto && !nofail && !automount)
285                 fprintf(f, "Before=%s\n", post);
286
287         if (passno != 0) {
288                 r = generator_write_fsck_deps(f, arg_dest, what, where, fstype);
289                 if (r < 0)
290                         return r;
291         }
292
293         fprintf(f,
294                 "\n"
295                 "[Mount]\n"
296                 "What=%s\n"
297                 "Where=%s\n",
298                 what,
299                 where);
300
301         if (!isempty(fstype) && !streq(fstype, "auto"))
302                 fprintf(f, "Type=%s\n", fstype);
303
304         r = generator_write_timeouts(arg_dest, what, where, opts, &filtered);
305         if (r < 0)
306                 return r;
307
308         if (!isempty(filtered) && !streq(filtered, "defaults"))
309                 fprintf(f, "Options=%s\n", filtered);
310
311         fflush(f);
312         if (ferror(f)) {
313                 log_error("Failed to write unit file %s: %m", unit);
314                 return -errno;
315         }
316
317         if (!noauto && post) {
318                 lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
319                 if (!lnk)
320                         return log_oom();
321
322                 mkdir_parents_label(lnk, 0755);
323                 if (symlink(unit, lnk) < 0) {
324                         log_error("Failed to create symlink %s: %m", lnk);
325                         return -errno;
326                 }
327         }
328
329         if (automount) {
330                 automount_name = unit_name_from_path(where, ".automount");
331                 if (!automount_name)
332                         return log_oom();
333
334                 automount_unit = strjoin(arg_dest, "/", automount_name, NULL);
335                 if (!automount_unit)
336                         return log_oom();
337
338                 fclose(f);
339                 f = fopen(automount_unit, "wxe");
340                 if (!f) {
341                         log_error("Failed to create unit file %s: %m", automount_unit);
342                         return -errno;
343                 }
344
345                 fprintf(f,
346                         "# Automatically generated by systemd-fstab-generator\n\n"
347                         "[Unit]\n"
348                         "SourcePath=%s\n"
349                         "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
350                         source);
351
352                 if (post)
353                         fprintf(f,
354                                 "Before=%s\n",
355                                 post);
356
357                 fprintf(f,
358                         "[Automount]\n"
359                         "Where=%s\n",
360                         where);
361
362                 fflush(f);
363                 if (ferror(f)) {
364                         log_error("Failed to write unit file %s: %m", automount_unit);
365                         return -errno;
366                 }
367
368                 free(lnk);
369                 lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL);
370                 if (!lnk)
371                         return log_oom();
372
373                 mkdir_parents_label(lnk, 0755);
374                 if (symlink(automount_unit, lnk) < 0) {
375                         log_error("Failed to create symlink %s: %m", lnk);
376                         return -errno;
377                 }
378         }
379
380         return 0;
381 }
382
383 static int parse_fstab(bool initrd) {
384         _cleanup_endmntent_ FILE *f = NULL;
385         const char *fstab_path;
386         struct mntent *me;
387         int r = 0;
388
389         fstab_path = initrd ? "/sysroot/etc/fstab" : "/etc/fstab";
390         f = setmntent(fstab_path, "re");
391         if (!f) {
392                 if (errno == ENOENT)
393                         return 0;
394
395                 log_error("Failed to open %s: %m", fstab_path);
396                 return -errno;
397         }
398
399         while ((me = getmntent(f))) {
400                 _cleanup_free_ char *where = NULL, *what = NULL;
401                 int k;
402
403                 if (initrd && !mount_in_initrd(me))
404                         continue;
405
406                 what = fstab_node_to_udev_node(me->mnt_fsname);
407                 if (!what)
408                         return log_oom();
409
410                 if (detect_container(NULL) > 0 && is_device_path(what)) {
411                         log_info("Running in a container, ignoring fstab device entry for %s.", what);
412                         continue;
413                 }
414
415                 where = initrd ? strappend("/sysroot/", me->mnt_dir) : strdup(me->mnt_dir);
416                 if (!where)
417                         return log_oom();
418
419                 if (is_path(where))
420                         path_kill_slashes(where);
421
422                 log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type);
423
424                 if (streq(me->mnt_type, "swap"))
425                         k = add_swap(what, me);
426                 else {
427                         bool noauto, nofail, automount;
428                         const char *post;
429 #ifdef CONFIG_TIZEN_WIP
430                         _cleanup_free_ char *opts = NULL;
431 #endif
432
433                         noauto = !!hasmntopt(me, "noauto");
434                         nofail = !!hasmntopt(me, "nofail");
435                         automount =
436                                   hasmntopt(me, "comment=systemd.automount") ||
437                                   hasmntopt(me, "x-systemd.automount");
438
439 #ifdef CONFIG_TIZEN_WIP
440                         /* TODO */
441                         /* Tizen is now using switch-able smack
442                          * feature. On feature enabled, different
443                          * mount option is needed for tmpfs. This
444                          * problematic problem is solved in recent
445                          * util-linux. But we are not using mount of
446                          * util-linux. So to compat the both case, the
447                          * smackfsroot=* option will be conditionally
448                          * be parsed by fstab.
449                          */
450                         if (me->mnt_opts) {
451                                 _cleanup_free_ char *fs_new = NULL;
452                                 char *comment_start, *comment_end, *opt;
453                                 int n;
454
455                                 opts = strdup(me->mnt_opts);
456                                 assert(opts);
457
458                                 if (hasfs_comment(opts, &comment_start, &comment_end, &opt, &fs_new)) {
459                                         if (fs_new && use_smack())
460                                                 memmove(comment_start, opt, strlen(opt) + 1);
461                                         else
462                                                 memmove(comment_start, comment_end, strlen(comment_end) + 1);
463
464                                         n = strlen(opts);
465                                         if (n > 0 && opts[n - 1] == ',')
466                                                 opts[n - 1] = '\0';
467                                 }
468                         } else
469                                 opts = strdup("");
470 #endif
471
472                         if (initrd)
473                                 post = SPECIAL_INITRD_FS_TARGET;
474                         else if (mount_in_initrd(me))
475                                 post = SPECIAL_INITRD_ROOT_FS_TARGET;
476                         else if (mount_is_network(me))
477                                 post = SPECIAL_REMOTE_FS_TARGET;
478                         else
479                                 post = SPECIAL_LOCAL_FS_TARGET;
480
481                         k = add_mount(what,
482                                       where,
483                                       me->mnt_type,
484 #ifdef CONFIG_TIZEN_WIP
485                                       opts,
486 #else
487                                       me->mnt_opts,
488 #endif
489                                       me->mnt_passno,
490                                       noauto,
491                                       nofail,
492                                       automount,
493                                       post,
494                                       fstab_path);
495                 }
496
497                 if (k < 0)
498                         r = k;
499         }
500
501         return r;
502 }
503
504 static int add_root_mount(void) {
505         _cleanup_free_ char *what = NULL;
506         const char *opts;
507
508         if (isempty(arg_root_what)) {
509                 log_debug("Could not find a root= entry on the kernel commandline.");
510                 return 0;
511         }
512
513         what = fstab_node_to_udev_node(arg_root_what);
514         if (!path_is_absolute(what)) {
515                 log_debug("Skipping entry what=%s where=/sysroot type=%s", what, strna(arg_root_fstype));
516                 return 0;
517         }
518
519         if (!arg_root_options)
520                 opts = arg_root_rw > 0 ? "rw" : "ro";
521         else if (arg_root_rw >= 0 ||
522                  (!mount_test_option(arg_root_options, "ro") &&
523                   !mount_test_option(arg_root_options, "rw")))
524                 opts = strappenda(arg_root_options, ",", arg_root_rw > 0 ? "rw" : "ro");
525         else
526                 opts = arg_root_options;
527
528         log_debug("Found entry what=%s where=/sysroot type=%s", what, strna(arg_root_fstype));
529         return add_mount(what,
530                          "/sysroot",
531                          arg_root_fstype,
532                          opts,
533                          1,
534                          false,
535                          false,
536                          false,
537                          SPECIAL_INITRD_ROOT_FS_TARGET,
538                          "/proc/cmdline");
539 }
540
541 static int parse_proc_cmdline_item(const char *key, const char *value) {
542         int r;
543
544         /* root= and roofstype= may occur more than once, the last
545          * instance should take precedence.  In the case of multiple
546          * rootflags= the arguments should be concatenated */
547
548         if (STR_IN_SET(key, "fstab", "rd.fstab") && value) {
549
550                 r = parse_boolean(value);
551                 if (r < 0)
552                         log_warning("Failed to parse fstab switch %s. Ignoring.", value);
553                 else
554                         arg_fstab_enabled = r;
555
556         } else if (streq(key, "root") && value) {
557
558                 free(arg_root_what);
559                 arg_root_what = strdup(value);
560                 if (!arg_root_what)
561                         return log_oom();
562
563         } else if (streq(key, "rootfstype") && value) {
564
565                 free(arg_root_fstype);
566                 arg_root_fstype = strdup(value);
567                 if (!arg_root_fstype)
568                         return log_oom();
569
570         } else if (streq(key, "rootflags") && value) {
571                 char *o;
572
573                 o = arg_root_options ?
574                         strjoin(arg_root_options, ",", value, NULL) :
575                         strdup(value);
576                 if (!o)
577                         return log_oom();
578
579                 free(arg_root_options);
580                 arg_root_options = o;
581
582         } else if (streq(key, "rw") && !value)
583                 arg_root_rw = true;
584         else if (streq(key, "ro") && !value)
585                 arg_root_rw = false;
586
587         return 0;
588 }
589
590 int main(int argc, char *argv[]) {
591         int r = 0;
592
593         if (argc > 1 && argc != 4) {
594                 log_error("This program takes three or no arguments.");
595                 return EXIT_FAILURE;
596         }
597
598         if (argc > 1)
599                 arg_dest = argv[1];
600
601         log_set_target(LOG_TARGET_SAFE);
602         log_parse_environment();
603         log_open();
604
605         umask(0022);
606
607         if (parse_proc_cmdline(parse_proc_cmdline_item) < 0)
608                 return EXIT_FAILURE;
609
610         /* Always honour root= in the kernel command line if we are in an initrd */
611         if (in_initrd())
612                 r = add_root_mount();
613
614         /* Honour /etc/fstab only when that's enabled */
615         if (arg_fstab_enabled) {
616                 int k;
617
618                 log_debug("Parsing /etc/fstab");
619
620                 /* Parse the local /etc/fstab, possibly from the initrd */
621                 k = parse_fstab(false);
622                 if (k < 0)
623                         r = k;
624
625                 /* If running in the initrd also parse the /etc/fstab from the host */
626                 if (in_initrd()) {
627                         log_debug("Parsing /sysroot/etc/fstab");
628
629                         k = parse_fstab(true);
630                         if (k < 0)
631                                 r = k;
632                 }
633         }
634
635         free(arg_root_what);
636
637         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
638 }