discover/syslinux-parser: filter out duplicate conf files
[petitboot] / discover / syslinux-parser.c
index 8aef9c3b5ea878fefce296bb6e628f1edf2db830..be7b94aa5d5b2ccb625ebea1fc23212fda8784c2 100644 (file)
@@ -36,6 +36,11 @@ struct syslinux_options {
        char *cfg_dir;
 };
 
+struct conf_file_stat {
+       char *name;
+       struct stat stat;
+       struct list_item list;
+};
 
 static const char *const syslinux_conf_files[] = {
        "/boot/syslinux/syslinux.cfg",
@@ -83,7 +88,7 @@ static const char *const syslinux_ignored_names[] = {
        "f2",
        "f3",
        "f4",
-       "f5"
+       "f5",
        "f6",
        "f7",
        "f8",
@@ -285,7 +290,7 @@ static void syslinux_process_pair(struct conf_context *conf, const char *name, c
 static void syslinux_finalize(struct conf_context *conf)
 {
        struct syslinux_options *state = conf->parser_info;
-       struct syslinux_boot_option *syslinux_opt;
+       struct syslinux_boot_option *syslinux_opt, *tmp;
        struct discover_context *dc = conf->dc;
        struct discover_boot_option *d_opt;
        bool implicit_image = true;
@@ -404,16 +409,24 @@ static void syslinux_finalize(struct conf_context *conf)
 
                discover_context_add_boot_option(dc, d_opt);
                continue;
+
 fail:
                talloc_free(d_opt);
        }
+
+       list_for_each_entry_safe(&state->processed_options, syslinux_opt, tmp, list)
+               talloc_free(syslinux_opt);
+       list_init(&state->processed_options);
 }
 
 static int syslinux_parse(struct discover_context *dc)
 {
+       struct conf_file_stat *confcmp, *confdat;
+       struct list processed_conf_files;
        struct syslinux_options *state;
        const char * const *filename;
        struct conf_context *conf;
+       struct stat statbuf;
        char *cfg_dir;
        int len, rc;
        char *buf;
@@ -436,6 +449,8 @@ static int syslinux_parse(struct discover_context *dc)
        conf->parser_info = state = talloc_zero(conf, struct syslinux_options);
        list_init(&state->processed_options);
 
+       list_init(&processed_conf_files);
+
        /*
         * set the global defaults
         * by spec 'default' defaults to 'linux' and
@@ -448,10 +463,37 @@ static int syslinux_parse(struct discover_context *dc)
        conf_set_global_option(conf, "append", "");
 
        for (filename = syslinux_conf_files; *filename; filename++) {
+               /*
+                * guard against duplicate entries in case-insensitive
+                * filesystems, mainly vfat boot partitions
+                */
+               rc = parser_stat_path(dc, dc->device, *filename, &statbuf);
+               if (rc)
+                       continue;
+
+               rc = 0;
+
+               list_for_each_entry(&processed_conf_files, confcmp, list) {
+                       if (confcmp->stat.st_ino == statbuf.st_ino) {
+                               pb_log("conf file %s is a path duplicate of %s..skipping\n",
+                                      *filename, confcmp->name);
+                               rc = 1;
+                               break;
+                       }
+               }
+
+               if (rc)
+                       continue;
+
                rc = parser_request_file(dc, dc->device, *filename, &buf, &len);
                if (rc)
                        continue;
 
+               confdat = talloc_zero(conf, struct conf_file_stat);
+               confdat->stat = statbuf;
+               confdat->name = talloc_strdup(confdat, *filename);
+               list_add(&processed_conf_files, &confdat->list);
+
                /*
                 * save location of root config file for possible
                 * INCLUDE directives later