/*
 * Copyright (C) 2014 Colin Walters <walters@verbum.org>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; either version 2 of the licence or (at
 * your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General
 * Public License along with this library. If not, see <https://www.gnu.org/licenses/>.
 */

#include "config.h"

#include "ostree-bootloader-grub2.h"
#include "ostree-sysroot-private.h"
#include "otutil.h"
#include <gio/gfiledescriptorbased.h>
#include <gio/gunixoutputstream.h>
#include <sys/mount.h>
#include <glib/gstdio.h>
#include <ctype.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
// Written by bootupd
#define BOOTUPD_CONFIG "boot/bootupd-state.json"
// Horrible hack, to avoid including a JSON parser we just grep for this
#define BOOTUPD_CONFIG_STATIC_JSON_FRAGMENT "\"static-configs\""

/* Maintain backwards compatibility with legacy GRUB
 * installations that might rely on the -16 suffix
 * for real-mode booting.
 * Allow us to override this at build time if we're
 * using a modern GRUB installation.
 */
//#if !defined(WITH_MODERN_GRUB) && (defined(__i386__) || defined(__x86_64__))
// #define GRUB2_SUFFIX "16"
// #else
// #define GRUB2_SUFFIX ""
// #endif
#define GRUB2_SUFFIX ""
/* Maintain backwards compatibility with legacy GRUB
 * installations that might rely on the -efi suffix
 * for RHEL/fedora-based distros.
 * Allow us to override this at build time if we're
 * using a modern GRUB installation that makes the
 * suffix-less linux/initrd commands synonymous for
 * both EFI and BIOS booting.
 */
#if defined(WITH_MODERN_GRUB) || defined(__aarch64__)
#define GRUB2_EFI_SUFFIX ""
#else
#define GRUB2_EFI_SUFFIX "efi"
#endif

struct _OstreeBootloaderGrub2
{
  GObject parent_instance;

  OstreeSysroot *sysroot;
  GFile *config_path_bios_1;
  GFile *config_path_bios_2;
  GFile *config_path_efi;
  gboolean is_efi;
};

typedef GObjectClass OstreeBootloaderGrub2Class;

static void _ostree_bootloader_grub2_bootloader_iface_init (OstreeBootloaderInterface *iface);
G_DEFINE_TYPE_WITH_CODE (OstreeBootloaderGrub2, _ostree_bootloader_grub2, G_TYPE_OBJECT,
                         G_IMPLEMENT_INTERFACE (OSTREE_TYPE_BOOTLOADER,
                                                _ostree_bootloader_grub2_bootloader_iface_init));

static gboolean
_ostree_bootloader_grub2_query (OstreeBootloader *bootloader, gboolean *out_is_active,
                                GCancellable *cancellable, GError **error)
{
  OstreeBootloaderGrub2 *self = OSTREE_BOOTLOADER_GRUB2 (bootloader);

  g_autoptr (GFile) bootupd_config
      = g_file_resolve_relative_path (self->sysroot->path, BOOTUPD_CONFIG);
  if (g_file_query_exists (bootupd_config, NULL))
    {
      g_autofree char *bootupd_config_contents = NULL;
      if (!g_file_load_contents (bootupd_config, cancellable, &bootupd_config_contents, NULL, NULL,
                                 error))
        return glnx_prefix_error (error, "Failed to read bootupd config");
      if (strstr (bootupd_config_contents, BOOTUPD_CONFIG_STATIC_JSON_FRAGMENT) != NULL)
        {
          g_debug ("Found static bootupd config");
          *out_is_active = FALSE;
          return TRUE;
        }
    }

  /* Look for the BIOS path first */
  if (g_file_query_exists (self->config_path_bios_1, NULL)
      || g_file_query_exists (self->config_path_bios_2, NULL))
    {
      /* If we found it, we're done */
      *out_is_active = TRUE;
      return TRUE;
    }

  g_autoptr (GFile) efi_basedir
      = g_file_resolve_relative_path (self->sysroot->path, "boot/efi/EFI");

  g_clear_object (&self->config_path_efi);

  if (g_file_query_exists (efi_basedir, NULL))
    {
      g_autoptr (GFileEnumerator) direnum = NULL;

      direnum = g_file_enumerate_children (efi_basedir, OSTREE_GIO_FAST_QUERYINFO,
                                           G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error);
      if (!direnum)
        return FALSE;

      while (TRUE)
        {
          GFileInfo *file_info;
          const char *fname;

          if (!g_file_enumerator_iterate (direnum, &file_info, NULL, cancellable, error))
            return FALSE;
          if (file_info == NULL)
            break;

          fname = g_file_info_get_name (file_info);
          if (strcmp (fname, "BOOT") == 0)
            continue;

          if (g_file_info_get_file_type (file_info) != G_FILE_TYPE_DIRECTORY)
            continue;

          g_autofree char *subdir_grub_cfg
              = g_build_filename (gs_file_get_path_cached (efi_basedir), fname, "grub.cfg", NULL);

          if (g_file_test (subdir_grub_cfg, G_FILE_TEST_EXISTS))
            {
              self->config_path_efi = g_file_new_for_path (subdir_grub_cfg);
              break;
            }
        }

      /* If we found the EFI path, we're done */
      if (self->config_path_efi)
        {
          self->is_efi = TRUE;
          *out_is_active = TRUE;
          return TRUE;
        }
    }
  else
    *out_is_active = FALSE;

  return TRUE;
}

static const char *
_ostree_bootloader_grub2_get_name (OstreeBootloader *bootloader)
{
  return "grub2";
}

static const char *
_ostree_update_vmlinuz_sub(gchar *output_str, const char *title, const char *quoted_uuid,
                            const char *linuxTotal, const char *initrdTotal)
{
  GString *vmlinuz_update_sub = g_string_new(NULL);
  g_auto(GStrv) vmlinuz_tokens = g_strsplit(linuxTotal, " ", -1);
  g_auto(GStrv) initrd_tokens = g_strsplit(initrdTotal, " ", -1);

  if (g_strv_length(vmlinuz_tokens) < 2 || g_strv_length(initrd_tokens) < 2)
    return g_string_free(vmlinuz_update_sub, FALSE);

  char advance_title[256] = {0};
  snprintf(advance_title, sizeof(advance_title), "Advanced options for %s", title);

  g_auto(GStrv) old_output = g_strsplit(output_str, "\n", -1);

  GRegex *regex_title = g_regex_new("(menuentry\\s+')([^']+)('.*)", 0, 0, NULL);
  GRegex *regex_vmlinuz = g_regex_new("/ostree/[^ ]+/vmlinuz[^ ]*", 0, 0, NULL);
  GRegex *regex_initrd = g_regex_new("/ostree/[^ ]+/(initr(amfs|d)[^ ]+)", 0, 0, NULL);

  g_string_append_printf(vmlinuz_update_sub, "submenu %s $menuentry_id_option %s {\n",
                          g_shell_quote(advance_title), quoted_uuid);

  for (int i = 0; i < g_strv_length(vmlinuz_tokens); i++) {
    /* 从内核路径提取版本号 (e.g., 6.6.0-35-generic) */
    g_autofree gchar *kernel_version = NULL;
    if (g_regex_match(regex_vmlinuz, vmlinuz_tokens[i], 0, NULL)) {
      g_autofree char *basename = g_path_get_basename(vmlinuz_tokens[i]);
      if (g_str_has_prefix(basename, "vmlinuz-")) {
          kernel_version = g_strdup(basename + strlen("vmlinuz-")); /* Skip "vmlinuz-" prefix */
      }
    }

    for (int j = 0; old_output[j] != NULL; j++) {
      g_autofree char *replaced_str = NULL;
   
      if (g_regex_match(regex_title, old_output[j], 0, NULL) && kernel_version) {/* 修改菜单标题：添加内核版本 */
        g_autofree char *new_title = g_strdup_printf("%s, %s", title, kernel_version);
        g_autofree char *replacement = g_strdup_printf("\\1%s\\3", new_title);
        replaced_str = g_regex_replace(regex_title, old_output[j], -1, 0, replacement, 0, NULL);
      }else if (g_regex_match(regex_vmlinuz, old_output[j], 0, NULL)) {/*替换内核路径*/
        replaced_str = g_regex_replace(regex_vmlinuz, old_output[j], -1, 0, vmlinuz_tokens[i], 0, NULL);
      } else if (g_regex_match(regex_initrd, old_output[j], 0, NULL)) {/*替换initrd路径*/
        replaced_str = g_regex_replace(regex_initrd, old_output[j], -1, 0, initrd_tokens[i], 0, NULL);
      }

      if (replaced_str) {
        g_string_append(vmlinuz_update_sub, replaced_str);
      } else {
        g_string_append(vmlinuz_update_sub, old_output[j]);
      }
      g_string_append_c(vmlinuz_update_sub, '\n');
    }
  }

  g_string_append(vmlinuz_update_sub, "}\n");

  g_regex_unref(regex_title);
  g_regex_unref(regex_vmlinuz);
  g_regex_unref(regex_initrd);

  return g_string_free(vmlinuz_update_sub, FALSE);
}

static char*
extract_version_part(const char *title){
  if (!title)
    return NULL;
    
  gchar **parts = g_strsplit(title, " ", -1);
  char *result = NULL;

  if (g_strv_length(parts) >= 4) {
    result = g_strdup_printf("%s %s", parts[2], parts[3]);
  } else if (g_strv_length(parts) == 3) {
    result = g_strdup(parts[2]);
  }
  g_strfreev(parts);
  
  return result;
}
static gchar* handle_system_restore_opt(const gchar* inputStr) {
  /* 【系统还原选项】 grub条目：System Restore，grub参数：linux行末尾加上restore */

  GString *outputStr = g_string_new(NULL);
  g_auto(GStrv) old_output = g_strsplit(inputStr, "\n", -1);

  GRegex *title_regex = g_regex_new("(menuentry\\s+')([^']*)('\\s+--class)",  G_REGEX_DOTALL, 0, NULL);
  GRegex *regex_vmlinuz = g_regex_new("/ostree/[^ ]+/vmlinuz[^ ]*", 0, 0, NULL);

  for (int index = 0; old_output[index] != NULL; index++) {
    g_autofree char *replaced_str = NULL;
    gboolean is_linux_line = FALSE;

    if(g_regex_match(title_regex, old_output[index], 0, NULL)){
        GMatchInfo *match_info = NULL;
        g_regex_match(title_regex, old_output[index], 0, &match_info);
        gchar *full_title = g_match_info_fetch(match_info, 2);
        char *version_part = extract_version_part(full_title);
        replaced_str = g_regex_replace(title_regex, old_output[index], -1, 0, g_strdup_printf("\\1%s %s\\3", _("System Restore - openkylin v2.0sp2"), version_part ?: ""), 0, NULL);
        g_free(full_title);
        g_match_info_free(match_info);
    }else if (g_regex_match(regex_vmlinuz, old_output[index], 0, NULL)) {
        is_linux_line = TRUE;
    }

    if (replaced_str) {
        g_string_append(outputStr, replaced_str);
    } else {
        g_string_append(outputStr, old_output[index]);
    }

    if(is_linux_line){
      g_string_append(outputStr, "restore");
      is_linux_line = FALSE;
    }

    g_string_append_c(outputStr, '\n');
  }

  g_regex_unref(title_regex);
  g_regex_unref(regex_vmlinuz);

  return g_string_free(outputStr, FALSE);
}

static gchar* handle_restore_menu_opt(const gchar* inputStr) {
  GString *outputStr = g_string_new(NULL);
  g_auto(GStrv) old_output = g_strsplit(inputStr, "\n", -1);

  GRegex *title_regex = g_regex_new("(menuentry\\s+')([^']*)('\\s+--class)",  G_REGEX_DOTALL, 0, NULL);
  GRegex *regex_vmlinuz = g_regex_new("/ostree/[^ ]+/vmlinuz[^ ]*", 0, 0, NULL);

  for (int index = 0; old_output[index] != NULL; index++) {
    g_autofree char *replaced_str = NULL;
    if (g_regex_match(title_regex, old_output[index], 0, NULL)) {
        GMatchInfo *match_info = NULL;
        g_regex_match(title_regex, old_output[index], 0, &match_info);
        gchar *full_title = g_match_info_fetch(match_info, 2);
        char *version_part = extract_version_part(full_title);
        replaced_str = g_regex_replace(title_regex, old_output[index], -1, 0, g_strdup_printf("\\1%s %s\\3", _("System Restore Mode - openkylin v2.0sp2"), version_part ?: ""), 0, NULL);
        g_free(full_title);
        g_match_info_free(match_info);
        g_string_append(outputStr, replaced_str);
        g_string_append_c(outputStr, '\n');
        break;
    }
  }

  g_regex_unref(title_regex);
  g_regex_unref(regex_vmlinuz);

  gchar *current = outputStr->str;
  gsize old_len = strlen("menuentry");
  gsize new_len = strlen("submenu");
  while ((current = strstr(current, "menuentry")) != NULL) {
    gsize pos = current - outputStr->str;
    g_string_erase(outputStr, pos, old_len);
    g_string_insert(outputStr, pos, "submenu");
    current = outputStr->str + pos + new_len;
  }

  const char *second_quote = strchr(outputStr->str, '\'');
  if (second_quote) {
    second_quote = strchr(second_quote + 1, '\''); 
  }
  const char *unrestricted_start = strstr(second_quote + 1, "--unrestricted");
  size_t start_pos = second_quote - outputStr->str + 1;
  size_t end_pos = unrestricted_start - outputStr->str;
  if (end_pos > start_pos) {
    g_string_erase(outputStr, start_pos, end_pos - start_pos);
    g_string_insert_c(outputStr, start_pos, ' ');
  }

  return g_string_free(outputStr, FALSE);
}

/* 
 * @Description: Determine if the deployment is a factory branch
 * @param: OstreeSysroot
 * @return: OstreeBootconfigParser
*/ 
static gboolean
_ostree_bootconfg_parser_is_restore_factory(OstreeSysroot *sysroot,
                                            OstreeBootconfigParser *config)
{
  g_autoptr(GPtrArray) deployments = ostree_sysroot_get_deployments(sysroot);

  if (deployments->len == 0)
  {
    return FALSE;
  }
  else
  {
    for (guint i = 0; i < deployments->len; i++)
    {
      OstreeDeployment *deployment = deployments->pdata[i];
      int deploy_version =atoi(ostree_bootconfig_parser_get (config,OSTREE_COMMIT_META_KEY_VERSION));
      if(ostree_deployment_get_index(deployment) == deployments->len - deploy_version)//找到了conf对应的deplotment
      {
        if(ostree_deployment_is_pinned(deployment))
          return TRUE;
      }
    }
    return FALSE;
  }
}

static char* trim_whitespace(char *str)
{/* 该函数用途：去除字符串两端空白 */

    if (!str) return NULL;
    
    /* 去除前导空白 */
    char *start = str;
    while (*start && isspace((unsigned char)*start)) {
        start++;
    }
    /* 全空白字符串 */
    if (*start == '\0') {
        return start;
    }
    /* 去除尾部空白 */
    char *end = start + strlen(start) - 1;
    while (end > start && isspace((unsigned char)*end)) {
        end--;
    }
    *(end + 1) = '\0';
    
    return start;
}

static char* get_value_by_key_from_file(const char* filename, const char* key)
{
    FILE* file = fopen(filename, "r");
    if (!file)
        return NULL;

    char* line = NULL;
    size_t len = 0;
    ssize_t read;
    char* result = NULL;

    while ((read = getline(&line, &len, file)) != -1) {
        /* 查找键名位置 */
        char* key_start = strstr(line, key);
        if (!key_start) continue;

        /* 验证键名格式 */
        char* colon = strchr(key_start, ':');
        if (!colon) continue;

        /* 检查键名前是否有效 */
        if (key_start != line) {
            char prev_char = *(key_start - 1);
            /* 确保键名是独立单词 */
            if (!isspace((unsigned char)prev_char)) continue;
        }

        /* 提取值部分 */
        char* value_start = colon + 1;
        char* value = trim_whitespace(value_start); /* 去除字符串两端空白 */

        /* 处理可能的引号 */
        size_t val_len = strlen(value);
        if (val_len >= 2 && value[0] == '"' && value[val_len-1] == '"') {
            value[val_len-1] = '\0';
            result = strdup(value + 1);
        } else {
            result = strdup(value);
        }
        break;
    }

    free(line);
    fclose(file);
    return result;
}

static char *get_appScene(void)
{/* 先检查 /etc/LICENS 文件，尝试从中提取 APP_SCENE 字段的值；若未提取到则再检查 /etc/.kylin-osinfo 文件，尝试从中提取 APP_SCENE 字段的值。*/

    char *license_appScene = get_value_by_key_from_file("/etc/LICENSE", "APP_SCENE");
    if(license_appScene){
        g_debug("get %s by APP_SCENE from /etc/LICENSE", license_appScene);
        return license_appScene;
    }
    char *osinfo_appScene = get_value_by_key_from_file("/etc/.kylin-osinfo", "APP_SCENE");
    if(osinfo_appScene){
        g_debug("get %s by APP_SCENE from /etc/.kylin-osinfo", osinfo_appScene);
        return osinfo_appScene;
    }
    
    return NULL;
}

static char* insert_identifier(const char *origin_title, const char* identifier)
{
    const char* ver = strstr(origin_title, "v11");
    return ver ? g_strdup_printf("%.*s%s %s", (int)(ver - origin_title), origin_title, identifier, ver) : g_strdup(origin_title);
}

/* 返回：需调用者使用 g_free() 释放的字符串 */
static char *get_deploy_datetime(OstreeSysroot *sysroot, OstreeBootconfigParser *config){
    g_autoptr(GPtrArray) deployments = ostree_sysroot_get_deployments(sysroot);

    if (deployments->len == 0){
        return NULL;
    }
    /* 获取版本号（添加错误检查） */
    const char *version_str = ostree_bootconfig_parser_get(config, OSTREE_COMMIT_META_KEY_VERSION);
    if (!version_str) {
        g_debug("Missing version metadata in bootconfig");
        return NULL;
    }
    int deploy_version = atoi(version_str);
    g_debug("Matching deployment: total=%d, target_version=%d", deployments->len, deploy_version);
    for (guint i = 0; i < deployments->len; i++)
    {
        OstreeDeployment *deployment = deployments->pdata[i];
        if(ostree_deployment_get_index(deployment) == deployments->len - deploy_version)/* 找到了conf对应的deplotment */
        {
            GKeyFile *origin = ostree_deployment_get_origin (deployment);
            if(origin){/* 获取部署时间 */
                char *deploy_datetime_str = g_key_file_get_string (origin, "origin", "deploy_datetime", NULL);
                g_debug("deploy datetime : %s", deploy_datetime_str);
                return deploy_datetime_str;
            }
        }
    }
    return NULL;
}

/* This implementation is quite complex; see this issue for
 * a starting point:
 * https://github.com/ostreedev/ostree/issues/717
 */
gboolean
_ostree_bootloader_grub2_generate_config (OstreeSysroot *sysroot, int bootversion, int target_fd,
                                          GCancellable *cancellable, GError **error)
{
  /* So... yeah.  Just going to hardcode these. */
  #if defined(__aarch64__) || defined(__arm__)
    static const char hardcoded_video[] = "";
  #else
    static const char hardcoded_video[] = "load_video\n"
      "set gfxpayload=keep\n";
  #endif
  static const char hardcoded_insmods[] = "insmod gzio\n";
  const char *grub2_boot_device_id = g_getenv ("GRUB2_BOOT_DEVICE_ID");
  const char *grub2_prepare_root_cache = g_getenv ("GRUB2_PREPARE_ROOT_CACHE");
  const char *grub2_cmdline_linux_default = g_getenv ("GRUB_CMDLINE_LINUX_DEFAULT");
  const char *grub2_cmdline_linux_security = g_getenv ("GRUB_CMDLINE_LINUX_SECURITY");
  const char *grub2_cmdline_linux_upgrade = g_getenv ("GRUB_CMDLINE_LINUX_UPGRADE");
  //新增获取是否禁用还原选项的变量
  const char *grub_restore_disabled = g_getenv ("GRUB_RESTORE_DISABLED");
  gboolean is_restore_disabled = (grub_restore_disabled != NULL && g_strcmp0(grub_restore_disabled,"yes")==0);

  /* We must have been called via the wrapper script */
  g_assert (grub2_boot_device_id != NULL);
  g_assert (grub2_prepare_root_cache != NULL);

  /* Passed from the parent */
  gboolean is_efi = g_getenv ("_OSTREE_GRUB2_IS_EFI") != NULL;

  g_autoptr (GOutputStream) out_stream = g_unix_output_stream_new (target_fd, FALSE);

  g_autoptr (GPtrArray) loader_configs = NULL;
  if (!_ostree_sysroot_read_boot_loader_configs (sysroot, bootversion, &loader_configs, cancellable,
                                                 error))
    return FALSE;
  /* New addition:Determine whether it is necessary to add the "/boot" perfix before the kernel path*/
  gboolean need_boot_prefix = FALSE;

  g_autofree char *sysroot_path = g_file_get_path(ostree_sysroot_get_path(sysroot));

  if (sysroot_path)
  {
      g_autofree char *boot_path = g_build_filename(sysroot_path, "boot", NULL);
      struct stat boot_stat, root_stat;
      if(g_stat(boot_path, &boot_stat)==0 && g_stat(sysroot_path, &root_stat)==0)
      {
        if (boot_stat.st_dev == root_stat.st_dev)
        {
          need_boot_prefix = TRUE; 
        }
      }

  }
  /*Left bracket {flag bit*/
  gboolean left_brace = FALSE;
  /*Right bracket} flag bit*/
  gboolean right_brace = FALSE;
  /*multiple_vmlinuz flag bit*/
  gboolean multiple_vmlinuz_brace = FALSE;
  g_autoptr (GString) output = g_string_new ("");
  /*Restore is used to save and factory restore fields*/
  g_autoptr (GString) restore_menu = g_string_new ("");
  g_autoptr (GString) restore = g_string_new ("");
  g_autoptr (GString) system_restore = g_string_new ("");
  /*Temporarily save the content of the output*/
  g_autoptr (GString) tmp = g_string_new ("");
  for (guint i = 0; i < loader_configs->len; i++)
    {
      OstreeBootconfigParser *config = loader_configs->pdata[i];
      const char *title;
      const char *options;
      const char *kernel;
      const char *initrd;
      const char *devicetree;
      const char *linuxTotal;
      const char *initrdTotal;
      char *quoted_title = NULL;
      char *uuid = NULL;
      char *quoted_uuid = NULL;
      char *version_part = NULL;

      title = ostree_bootconfig_parser_get (config, "title");
      if (!title)
        title = "(Untitled)";

      version_part = extract_version_part(title);
      g_autofree char *app_scene = get_appScene();
      if(app_scene && strcmp(app_scene, "EDU") == 0){/* 如果是教育版，需要添加教育版标识 */
        title = g_strdup_printf("%s %s", _("openkylin v2.0sp2 EDU"), version_part);
        g_debug("insert EDU identifier, tiltle:%s\n", title);
      }
      else{
        title = g_strdup_printf("%s %s", _("openkylin v2.0sp2"), version_part);
      }

      /*Determine if the deployment corresponding to the current configuration is factory made*/
      if (_ostree_bootconfg_parser_is_restore_factory(sysroot, config)) 
      {
          if(is_restore_disabled)
          {
              continue;
          }
        char restore_title[100] = {0};
        sprintf(restore_title, "%s %s", _("Factory Restore - openkylin v2.0sp2"), version_part);
        title = restore_title;
        /*Set the boot for restoring the factory part, first save the original output in the temporary variable tmp, and then set the output to empty
        After obtaining all the factory recovery guides, set Restore to output to save the factory recovery string, and restore the output to tmp content.*/
        g_string_set_size(tmp,0);
        g_string_append (tmp, output->str);
        g_string_set_size(output,0);
      }
      else if(grub2_cmdline_linux_upgrade != NULL && strcmp(grub2_cmdline_linux_upgrade, "") != 0) //表示处于更新升级
      {
        char upgrade_title[100] = {0};
        sprintf(upgrade_title, "Upgrade to %s", title);
        title = upgrade_title;
      }else if(i > 0){
          g_autofree char* deploy_datetime_str = get_deploy_datetime(sysroot, config);
          if(deploy_datetime_str){
              char historical_subitme_title[100] = {0};
              sprintf(historical_subitme_title, "%s (%s)", title, deploy_datetime_str);
              title = historical_subitme_title;
              g_debug("historical version subitme title : %s", title);
          }
      }
      kernel = ostree_bootconfig_parser_get (config, "linux");

      quoted_title = g_shell_quote (title);
      uuid = g_strdup_printf ("ostree-%u-%s", (guint)i, grub2_boot_device_id);
      quoted_uuid = g_shell_quote (uuid);
      g_string_append_printf (output, "menuentry %s --class kylin --class gnu-linux --class gnu --class os --unrestricted %s {\n", quoted_title, quoted_uuid);

      /* Hardcoded sections */
      g_string_append (output, hardcoded_video);
      g_string_append (output, hardcoded_insmods);
      g_string_append (output, grub2_prepare_root_cache);
      g_string_append_c (output, '\n');

      if (!kernel)
      {
        g_free(uuid);
        g_free(quoted_title);
        g_free(quoted_uuid);
        return glnx_throw(error, "No \"linux\" key in bootloader config");
      }
      g_string_append (output, "linux");
      if (is_efi)
        g_string_append (output, GRUB2_EFI_SUFFIX);
      else
        g_string_append (output, GRUB2_SUFFIX);
      g_string_append_c (output, ' ');
      if (need_boot_prefix) {
        g_string_append (output, "/boot");
      }
      g_string_append (output, kernel);

      options = ostree_bootconfig_parser_get (config, "options");
      if (options)
        {
          g_string_append_c (output, ' ');
          g_string_append (output, options);
          
          g_string_append_c (output, ' ');
          g_string_append (output, grub2_cmdline_linux_default);

          g_string_append_c (output, ' ');
          g_string_append (output, grub2_cmdline_linux_security);
        }
      if (_ostree_bootconfg_parser_is_restore_factory(sysroot, config))
      {
        g_string_append (output, " factory-restore");
      }else{
        g_string_append_c (output, ' ');
        g_string_append (output, grub2_cmdline_linux_upgrade);
      }
      g_string_append_c (output, '\n');

      initrd = ostree_bootconfig_parser_get (config, "initrd");
      if (initrd)
        {
          g_string_append (output, "initrd");
          if (is_efi)
            g_string_append (output, GRUB2_EFI_SUFFIX);
          else
            g_string_append (output, GRUB2_SUFFIX);
          g_string_append_c (output, ' ');
          if (need_boot_prefix) {
            g_string_append (output, "/boot");
          }
          g_string_append (output, initrd);
          g_string_append_c (output, '\n');
        }

      devicetree = ostree_bootconfig_parser_get (config, "devicetree");
      if (devicetree)
        {
          g_string_append (output, "devicetree");
          g_string_append_c (output, ' ');
          g_string_append (output, devicetree);
          g_string_append_c (output, '\n'); 
        }

      g_string_append (output, "}\n");

      if(i == 0&&!is_restore_disabled){/* 处理系统还原grub选项内容 */
        g_autofree gchar *system_restore_opt = handle_system_restore_opt(output->str);
        if (system_restore_opt) {
          g_string_append(system_restore, system_restore_opt);
        }
        g_autofree gchar *restore_menu_opt = handle_restore_menu_opt(output->str);
        if (restore_menu_opt) {
          g_string_append(restore_menu, restore_menu_opt);
        }
      }

      linuxTotal = ostree_bootconfig_parser_get (config, "linuxTotal");
      initrdTotal = ostree_bootconfig_parser_get (config, "initrdTotal");

      if (!multiple_vmlinuz_brace && linuxTotal && initrdTotal && output->str != NULL)
      {
          multiple_vmlinuz_brace = TRUE;
          g_autofree char *vmlinuz_sub = _ostree_update_vmlinuz_sub(output->str, title , quoted_uuid, linuxTotal, initrdTotal);
          if (vmlinuz_sub)
            g_string_append(output, vmlinuz_sub);
      }

      /*Determine whether to increase{*/
      if (!left_brace && loader_configs->len > 2 && !_ostree_bootconfg_parser_is_restore_factory(sysroot,config))
      {
        char historty_title[1024] = {0};
        sprintf(historty_title, "%s %s", _("Historical Version -"), title);
        char *quoted_my_sub = NULL;
        quoted_my_sub = g_shell_quote(historty_title);
        g_string_append_printf(output, "submenu %s  --unrestricted %s {\n", quoted_my_sub, quoted_uuid);
        g_free(quoted_my_sub);
        left_brace = TRUE;
      }
      g_free(uuid);
      g_free(quoted_title);
      g_free(quoted_uuid);

      /*The factory version writing is complete. The output stores the factory version,\
       while the tmp stores the previously traversed version. Reset back*/
      if (_ostree_bootconfg_parser_is_restore_factory(sysroot, config)) 
      {
        g_string_append(restore,output->str);
        g_string_set_size(output,0);
        g_string_append(output,tmp->str);
      }
    }

    /*Determine whether to increase}*/
    if (left_brace && !right_brace && loader_configs->len > 2)
    {
      g_string_append(output, " }\n");
      right_brace = TRUE;
    }

    g_string_append_c (output, '\n');
    if (restore_menu->len&&!is_restore_disabled)
    {
      g_string_append(output, restore_menu->str);
    }

    /* 系统还原 */
    if(system_restore->len&&!is_restore_disabled){
      g_string_append(output, system_restore->str);
    }
    /* 出厂还原 */
    if (restore->len&&!is_restore_disabled)
    {
      g_string_append(output, restore->str);
    }
    if(!is_restore_disabled)
    {
        g_string_append(output, "}\n");
    }

  gsize bytes_written;
  if (!g_output_stream_write_all (out_stream, output->str, output->len, &bytes_written, cancellable,
                                  error))
    return FALSE;

  return TRUE;
}

typedef struct
{
  const char *root;
  const char *bootversion_str;
  gboolean is_efi;
} Grub2ChildSetupData;

/* Post-fork, pre-exec child setup for grub2-mkconfig */
static void
grub2_child_setup (gpointer user_data)
{
  Grub2ChildSetupData *cdata = user_data;

  setenv ("_OSTREE_GRUB2_BOOTVERSION", cdata->bootversion_str, TRUE);
  /* We have to pass our state (whether or not we're using EFI) to the child */
  if (cdata->is_efi)
    setenv ("_OSTREE_GRUB2_IS_EFI", "1", TRUE);

  /* Everything below this is dealing with the chroot case; if
   * we're not doing that, return early.
   */
  if (!cdata->root)
    return;

  /* TODO: investigate replacing this with bwrap */
  if (chdir (cdata->root) != 0)
    {
      perror ("chdir");
      _exit (1);
    }

  if (unshare (CLONE_NEWNS) != 0)
    {
      perror ("CLONE_NEWNS");
      _exit (1);
    }

  if (mount (NULL, "/", "none", MS_REC | MS_PRIVATE, NULL) < 0)
    {
      perror ("Failed to make / a private mount");
      _exit (1);
    }

  if (mount (".", ".", NULL, MS_BIND | MS_PRIVATE, NULL) < 0)
    {
      perror ("mount (MS_BIND)");
      _exit (1);
    }

  if (mount (cdata->root, "/", NULL, MS_MOVE, NULL) < 0)
    {
      perror ("failed to MS_MOVE to /");
      _exit (1);
    }

  if (chroot (".") != 0)
    {
      perror ("chroot");
      _exit (1);
    }
}

/* Main entrypoint for writing GRUB configuration. */
static gboolean
_ostree_bootloader_grub2_write_config (OstreeBootloader *bootloader, int bootversion,
                                       GPtrArray *new_deployments, GCancellable *cancellable,
                                       GError **error)
{
  OstreeBootloaderGrub2 *self = OSTREE_BOOTLOADER_GRUB2 (bootloader);

  /* Autotests can set this envvar to select which code path to test, useful for OS installers as
   * well */
  gboolean use_system_grub2_mkconfig = TRUE;
#ifdef USE_BUILTIN_GRUB2_MKCONFIG
  use_system_grub2_mkconfig = FALSE;
#endif
  const gchar *grub_exec = g_getenv ("OSTREE_GRUB2_EXEC");
  if (grub_exec)
    {
      if (g_str_has_suffix (grub_exec, GRUB2_MKCONFIG_PATH))
        use_system_grub2_mkconfig = TRUE;
      else
        use_system_grub2_mkconfig = FALSE;
    }
  else
    grub_exec = use_system_grub2_mkconfig ? GRUB2_MKCONFIG_PATH
                                          : TARGET_PREFIX "/lib/ostree/ostree-grub-generator";

  g_autofree char *grub2_mkconfig_chroot = NULL;
  if (use_system_grub2_mkconfig && ostree_sysroot_get_booted_deployment (self->sysroot) == NULL
      && g_file_has_parent (self->sysroot->path, NULL))
    {
      OstreeDeployment *tool_deployment;
      g_autoptr (GFile) tool_deployment_root = NULL;

      g_assert_cmpint (new_deployments->len, >, 0);
      tool_deployment = new_deployments->pdata[0];

      /* Sadly we have to execute code to generate the bootloader configuration.
       * If we're in a booted deployment, we just don't chroot.
       *
       * In the case of an installer, use the first deployment root (which
       * will most likely be the only one.
       *
       * This all only applies if we're not using the builtin
       * generator, which handles being run outside of the root.
       */
      tool_deployment_root
          = ostree_sysroot_get_deployment_directory (self->sysroot, tool_deployment);
      grub2_mkconfig_chroot = g_file_get_path (tool_deployment_root);
    }

  g_debug ("Using grub2-mkconfig chroot: %s\n", grub2_mkconfig_chroot);

  g_autoptr (GFile) new_config_path = NULL;
  g_autoptr (GFile) config_path_efi_dir = NULL;
  if (self->is_efi)
    {
      config_path_efi_dir = g_file_get_parent (self->config_path_efi);
      new_config_path = g_file_get_child (config_path_efi_dir, "grub.cfg.new");
      /* We use grub2-mkconfig to write to a temporary file first */
      if (!ot_gfile_ensure_unlinked (new_config_path, cancellable, error))
        return FALSE;
    }
  else
    {
      new_config_path = ot_gfile_resolve_path_printf (self->sysroot->path,
                                                      "boot/loader.%d/grub.cfg", bootversion);
    }

  const char *grub_argv[4] = { NULL, "-o", NULL, NULL };
  Grub2ChildSetupData cdata = {
    NULL,
  };
  grub_argv[0] = grub_exec;
  grub_argv[2] = gs_file_get_path_cached (new_config_path);

  GSpawnFlags grub_spawnflags = G_SPAWN_SEARCH_PATH;
  if (!g_getenv ("OSTREE_DEBUG_GRUB2"))
    grub_spawnflags |= G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL;
  cdata.root = grub2_mkconfig_chroot;
  g_autofree char *bootversion_str = g_strdup_printf ("%u", (guint)bootversion);
  cdata.bootversion_str = bootversion_str;
  cdata.is_efi = self->is_efi;
  /* Note in older versions of the grub2 package, this script doesn't even try
     to be atomic; it just does:

     cat ${grub_cfg}.new > ${grub_cfg}
     rm -f ${grub_cfg}.new

     Upstream is fixed though.
  */
  int grub2_estatus;
  if (!g_spawn_sync (NULL, (char **)grub_argv, NULL, grub_spawnflags, grub2_child_setup, &cdata,
                     NULL, NULL, &grub2_estatus, error))
    return FALSE;
  if (!g_spawn_check_exit_status (grub2_estatus, error))
    {
      g_prefix_error (error, "%s: ", grub_argv[0]);
      return FALSE;
    }

  /* Now let's fdatasync() for the new file */
  {
    glnx_autofd int new_config_fd = -1;
    if (!glnx_openat_rdonly (AT_FDCWD, gs_file_get_path_cached (new_config_path), TRUE,
                             &new_config_fd, error))
      return FALSE;

    if (fdatasync (new_config_fd) < 0)
      return glnx_throw_errno_prefix (error, "fdatasync");
  }

  if (self->is_efi)
    {
      g_autoptr (GFile) config_path_efi_old
          = g_file_get_child (config_path_efi_dir, "grub.cfg.old");

      /* copy current to old */
      if (!ot_gfile_ensure_unlinked (config_path_efi_old, cancellable, error))
        return FALSE;
      if (!g_file_copy (self->config_path_efi, config_path_efi_old, G_FILE_COPY_OVERWRITE,
                        cancellable, NULL, NULL, error))
        return FALSE;

      /* NOTE: NON-ATOMIC REPLACEMENT; WE can't do anything else on FAT;
       * see https://bugzilla.gnome.org/show_bug.cgi?id=724246
       */
      if (!ot_gfile_ensure_unlinked (self->config_path_efi, cancellable, error))
        return FALSE;
      if (rename (gs_file_get_path_cached (new_config_path),
                  gs_file_get_path_cached (self->config_path_efi))
          < 0)
        return glnx_throw_errno_prefix (error, "rename");
    }

  return TRUE;
}

static gboolean
_ostree_bootloader_grub2_is_atomic (OstreeBootloader *bootloader)
{
  OstreeBootloaderGrub2 *self = OSTREE_BOOTLOADER_GRUB2 (bootloader);
  return !self->is_efi;
}

static void
_ostree_bootloader_grub2_finalize (GObject *object)
{
  OstreeBootloaderGrub2 *self = OSTREE_BOOTLOADER_GRUB2 (object);

  g_clear_object (&self->sysroot);
  g_clear_object (&self->config_path_bios_1);
  g_clear_object (&self->config_path_bios_2);
  g_clear_object (&self->config_path_efi);

  G_OBJECT_CLASS (_ostree_bootloader_grub2_parent_class)->finalize (object);
}

void
_ostree_bootloader_grub2_init (OstreeBootloaderGrub2 *self)
{
}

static void
_ostree_bootloader_grub2_bootloader_iface_init (OstreeBootloaderInterface *iface)
{
  iface->query = _ostree_bootloader_grub2_query;
  iface->get_name = _ostree_bootloader_grub2_get_name;
  iface->write_config = _ostree_bootloader_grub2_write_config;
  iface->is_atomic = _ostree_bootloader_grub2_is_atomic;
}

void
_ostree_bootloader_grub2_class_init (OstreeBootloaderGrub2Class *class)
{
  GObjectClass *object_class = G_OBJECT_CLASS (class);

  object_class->finalize = _ostree_bootloader_grub2_finalize;
}

OstreeBootloaderGrub2 *
_ostree_bootloader_grub2_new (OstreeSysroot *sysroot)
{
  OstreeBootloaderGrub2 *self = g_object_new (OSTREE_TYPE_BOOTLOADER_GRUB2, NULL);
  self->sysroot = g_object_ref (sysroot);
  /* Used by (at least) Debian */
  self->config_path_bios_1
      = g_file_resolve_relative_path (self->sysroot->path, "boot/grub/grub.cfg");
  /* Used by (at least) Fedora */
  self->config_path_bios_2
      = g_file_resolve_relative_path (self->sysroot->path, "boot/grub2/grub.cfg");
  return self;
}
