From 713d31dfeb4425cfb40f565436504f4056ebe548 Mon Sep 17 00:00:00 2001 From: jake Date: Tue, 21 Nov 2023 02:45:37 +0000 Subject: [PATCH 14/22] !288 use atomic write for config, secomp, oci_hook files * use atomic write for config, secomp, oci_hook files --- src/conf.c | 2 +- src/lcrcontainer_extend.c | 150 ++++++++++----------- src/utils.c | 268 +++++++++++++++++++++++++++++++++++++- src/utils.h | 6 +- 4 files changed, 343 insertions(+), 83 deletions(-) diff --git a/src/conf.c b/src/conf.c index 10214f0..bfbfe1a 100644 --- a/src/conf.c +++ b/src/conf.c @@ -1241,7 +1241,7 @@ static int trans_one_oci_id_mapping(struct lcr_list *conf, const char *typ, cons if (nret < 0 || (size_t)nret >= sizeof(subid)) { return -1; } - nret = lcr_util_atomic_write_file(path, subid); + nret = lcr_util_flock_append_file(path, subid); if (nret < 0) { return -1; } diff --git a/src/lcrcontainer_extend.c b/src/lcrcontainer_extend.c index 321be8c..b3202a7 100644 --- a/src/lcrcontainer_extend.c +++ b/src/lcrcontainer_extend.c @@ -346,7 +346,7 @@ out: return ret; } -static int lcr_spec_write_seccomp_line(int fd, const char *seccomp) +static int lcr_spec_write_seccomp_line(FILE *fp, const char *seccomp) { size_t len; char *line = NULL; @@ -371,14 +371,17 @@ static int lcr_spec_write_seccomp_line(int fd, const char *seccomp) ERROR("Sprintf failed"); goto cleanup; } + if ((size_t)nret > len - 1) { nret = (int)(len - 1); } + line[nret] = '\n'; - if (write(fd, line, len) == -1) { + if (fwrite(line, 1, len ,fp) != len) { SYSERROR("Write file failed"); goto cleanup; } + ret = 0; cleanup: free(line); @@ -389,9 +392,7 @@ static char *lcr_save_seccomp_file(const char *bundle, const char *seccomp_conf) { char seccomp[PATH_MAX] = { 0 }; char *real_seccomp = NULL; - int fd = -1; int nret; - ssize_t written_cnt; nret = snprintf(seccomp, sizeof(seccomp), "%s/seccomp", bundle); if (nret < 0 || (size_t)nret >= sizeof(seccomp)) { @@ -404,16 +405,9 @@ static char *lcr_save_seccomp_file(const char *bundle, const char *seccomp_conf) goto cleanup; } - fd = lcr_util_open(real_seccomp, O_CREAT | O_TRUNC | O_CLOEXEC | O_WRONLY, CONFIG_FILE_MODE); - if (fd == -1) { - SYSERROR("Create file %s failed", real_seccomp); - goto cleanup; - } - - written_cnt = write(fd, seccomp_conf, strlen(seccomp_conf)); - close(fd); - if (written_cnt == -1) { - SYSERROR("write seccomp_conf failed"); + if (lcr_util_atomic_write_file(real_seccomp, seccomp_conf, strlen(seccomp_conf), + CONFIG_FILE_MODE, false) != -1) { + ERROR("write seccomp_conf failed"); goto cleanup; } return real_seccomp; @@ -605,34 +599,51 @@ out_free: return NULL; } - -static int lcr_open_config_file(const char *bundle) +static FILE *lcr_open_tmp_config_file(const char *bundle, char **config_file, char **tmp_file) { char config[PATH_MAX] = { 0 }; - char *real_config = NULL; int fd = -1; int nret; + FILE *fp = NULL; nret = snprintf(config, sizeof(config), "%s/config", bundle); if (nret < 0 || (size_t)nret >= sizeof(config)) { goto out; } - nret = lcr_util_ensure_path(&real_config, config); + nret = lcr_util_ensure_path(config_file, config); if (nret < 0) { ERROR("Failed to ensure path %s", config); goto out; } - fd = lcr_util_open(real_config, O_CREAT | O_TRUNC | O_CLOEXEC | O_WRONLY, CONFIG_FILE_MODE); + *tmp_file = lcr_util_get_random_tmp_file(*config_file); + if (*tmp_file == NULL) { + ERROR("Failed to get random tmp file for %s", *config_file); + goto out; + } + + fd = lcr_util_open(*tmp_file, O_CREAT | O_TRUNC | O_CLOEXEC | O_WRONLY, CONFIG_FILE_MODE); if (fd == -1) { - SYSERROR("Create file %s failed", real_config); - lcr_set_error_message(LCR_ERR_RUNTIME, "Create file %s failed", real_config); + SYSERROR("Create file %s failed", *tmp_file); + lcr_set_error_message(LCR_ERR_RUNTIME, "Create file %s failed", *tmp_file); + goto out; + } + + fp = fdopen(fd, "w"); + if (fp == NULL) { + ERROR("FILE open failed"); goto out; } + out: - free(real_config); - return fd; + if (fp == NULL) { + free(*tmp_file); + *tmp_file = NULL; + free(*config_file); + *config_file = NULL; + } + return fp; } // escape_string_encode unzip some escape characters @@ -698,18 +709,17 @@ static char *escape_string_encode(const char *src) return dst; } -static int lcr_spec_write_config(int fd, const struct lcr_list *lcr_conf) +static int lcr_spec_write_config(FILE *fp, const struct lcr_list *lcr_conf) { - struct lcr_list *it = NULL; size_t len; - char *line = NULL; - char *line_encode = NULL; int ret = -1; + struct lcr_list *it = NULL; + char *line_encode = NULL; + char *line = NULL; lcr_list_for_each(it, lcr_conf) { lcr_config_item_t *item = it->elem; int nret; - size_t encode_len; if (item != NULL) { if (strlen(item->value) > ((SIZE_MAX - strlen(item->name)) - 4)) { goto cleanup; @@ -722,7 +732,6 @@ static int lcr_spec_write_config(int fd, const struct lcr_list *lcr_conf) } nret = snprintf(line, len, "%s = %s", item->name, item->value); - if (nret < 0 || (size_t)nret >= len) { ERROR("Sprintf failed"); goto cleanup; @@ -734,19 +743,21 @@ static int lcr_spec_write_config(int fd, const struct lcr_list *lcr_conf) goto cleanup; } - encode_len = strlen(line_encode); + len = strlen(line_encode); + line_encode[len] = '\n'; - line_encode[encode_len] = '\n'; - if (write(fd, line_encode, encode_len + 1) == -1) { + if (fwrite(line_encode, 1, len + 1, fp) != len + 1) { SYSERROR("Write file failed"); goto cleanup; } + free(line); line = NULL; free(line_encode); line_encode = NULL; } } + ret = 0; cleanup: free(line); @@ -804,7 +815,9 @@ bool lcr_save_spec(const char *name, const char *lcrpath, const struct lcr_list const char *path = lcrpath ? lcrpath : LCRPATH; char *bundle = NULL; char *seccomp = NULL; - int fd = -1; + char *config_file = NULL; + char *tmp_file = NULL; + FILE *fp = NULL; int nret = 0; if (name == NULL) { @@ -829,71 +842,47 @@ bool lcr_save_spec(const char *name, const char *lcrpath, const struct lcr_list } } - fd = lcr_open_config_file(bundle); - if (fd == -1) { + fp = lcr_open_tmp_config_file(bundle, &config_file, &tmp_file); + if (fp == NULL) { goto out_free; } - if (lcr_spec_write_config(fd, lcr_conf)) { + if (lcr_spec_write_config(fp, lcr_conf)) { goto out_free; } if (seccomp_conf != NULL) { - nret = lcr_spec_write_seccomp_line(fd, seccomp); + nret = lcr_spec_write_seccomp_line(fp, seccomp); if (nret) { goto out_free; } } - bret = true; + fclose(fp); + fp = NULL; -out_free: - free(bundle); - free(seccomp); - if (fd != -1) { - close(fd); - } - - return bret; -} - -static int lcr_write_file(const char *path, const char *data, size_t len) -{ - char *real_path = NULL; - int fd = -1; - int ret = -1; - - if (path == NULL || strlen(path) == 0 || data == NULL || len == 0) { - return -1; - } - - if (lcr_util_ensure_path(&real_path, path) < 0) { - ERROR("Failed to ensure path %s", path); + nret = rename(tmp_file, config_file); + if (nret != 0) { + ERROR("Failed to rename old file %s to target %s", tmp_file, config_file); goto out_free; } - fd = lcr_util_open(real_path, O_CREAT | O_TRUNC | O_CLOEXEC | O_WRONLY, CONFIG_FILE_MODE); - if (fd == -1) { - ERROR("Create file %s failed", real_path); - lcr_set_error_message(LCR_ERR_RUNTIME, "Create file %s failed", real_path); - goto out_free; - } - - if (write(fd, data, len) == -1) { - SYSERROR("write data to %s failed", real_path); - goto out_free; - } - - ret = 0; + bret = true; out_free: - if (fd != -1) { - close(fd); + if (fp != NULL) { + fclose(fp); } - free(real_path); - return ret; -} + if (!bret && unlink(tmp_file) != 0 && errno != ENOENT) { + SYSERROR("Failed to remove temp file:%s", tmp_file); + } + free(config_file); + free(tmp_file); + free(seccomp); + free(bundle); + return bret; +} static bool lcr_write_ocihooks(const char *path, const oci_runtime_spec_hooks *hooks) { @@ -907,8 +896,9 @@ static bool lcr_write_ocihooks(const char *path, const oci_runtime_spec_hooks *h goto out_free; } - if (lcr_write_file(path, json_hooks, strlen(json_hooks)) == -1) { - SYSERROR("write json hooks failed"); + if (lcr_util_atomic_write_file(path, json_hooks, strlen(json_hooks), + CONFIG_FILE_MODE, false) == -1) { + ERROR("write json hooks failed"); goto out_free; } diff --git a/src/utils.c b/src/utils.c index df73985..68e9bc4 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1248,7 +1248,7 @@ out: return ret; } -int lcr_util_atomic_write_file(const char *filepath, const char *content) +int lcr_util_flock_append_file(const char *filepath, const char *content) { int fd; int ret = 0; @@ -1284,6 +1284,272 @@ out: return ret; } +static char *lcr_util_path_base(const char *path) +{ + char *dir = NULL; + int len = 0; + int i = 0; + + if (path == NULL) { + ERROR("invalid NULL param"); + return NULL; + } + + len = (int)strlen(path); + if (len == 0) { + return lcr_util_strdup_s("."); + } + + dir = lcr_util_strdup_s(path); + + // strip last slashes + for (i = len - 1; i >= 0; i--) { + if (dir[i] != '/') { + break; + } + dir[i] = '\0'; + } + + len = (int)strlen(dir); + if (len == 0) { + free(dir); + return lcr_util_strdup_s("/"); + } + + for (i = len - 1; i >= 0; i--) { + if (dir[i] == '/') { + break; + } + } + + if (i < 0) { + return dir; + } + + char *result = lcr_util_strdup_s(&dir[i + 1]); + free(dir); + return result; +} + +static char *lcr_util_path_dir(const char *path) +{ + char *dir = NULL; + int len = 0; + int i = 0; + + if (path == NULL) { + ERROR("invalid NULL param"); + return NULL; + } + + len = (int)strlen(path); + if (len == 0) { + return lcr_util_strdup_s("."); + } + + dir = lcr_util_strdup_s(path); + + for (i = len - 1; i > 0; i--) { + if (dir[i] == '/') { + dir[i] = 0; + break; + } + } + + if (i == 0 && dir[0] == '/') { + free(dir); + return lcr_util_strdup_s("/"); + } + + return dir; +} + +static int lcr_util_generate_random_str(char *id, size_t len) +{ + int fd = -1; + int num = 0; + size_t i; + const int m = 256; + + if (id == NULL) { + return -1; + } + + len = len / 2; + fd = open("/dev/urandom", O_RDONLY); + if (fd == -1) { + ERROR("Failed to open /dev/urandom"); + return -1; + } + for (i = 0; i < len; i++) { + int nret; + if (lcr_util_read_nointr(fd, &num, sizeof(int)) < 0) { + ERROR("Failed to read urandom value"); + close(fd); + return -1; + } + unsigned char rs = (unsigned char)(num % m); + nret = snprintf((id + i * 2), ((len - i) * 2 + 1), "%02x", (unsigned int)rs); + if (nret < 0 || (size_t)nret >= ((len - i) * 2 + 1)) { + ERROR("Failed to snprintf random string"); + close(fd); + return -1; + } + } + close(fd); + id[i * 2] = '\0'; + return 0; +} + +static char *lcr_util_path_join(const char *dir, const char *file) +{ + int nret = 0; + char path[PATH_MAX] = { 0 }; + char cleaned[PATH_MAX] = { 0 }; + + if (dir == NULL || file == NULL) { + ERROR("NULL dir or file failed"); + return NULL; + } + + nret = snprintf(path, PATH_MAX, "%s/%s", dir, file); + if (nret < 0 || (size_t)nret >= PATH_MAX) { + ERROR("dir or file too long failed"); + return NULL; + } + + if (cleanpath(path, cleaned, sizeof(cleaned)) == NULL) { + ERROR("Failed to clean path: %s", path); + return NULL; + } + + return lcr_util_strdup_s(cleaned); +} + +char *lcr_util_get_random_tmp_file(const char *fname) +{ +#define RANDOM_TMP_PATH 10 + int nret = 0; + char *result = NULL; + char *base = NULL; + char *dir = NULL; + char rpath[PATH_MAX] = { 0x00 }; + char random_tmp[RANDOM_TMP_PATH + 1] = { 0x00 }; + + if (fname == NULL) { + ERROR("Invalid NULL param"); + return NULL; + } + + base = lcr_util_path_base(fname); + if (base == NULL) { + ERROR("Failed to get base of %s", fname); + goto out; + } + + dir = lcr_util_path_dir(fname); + if (dir == NULL) { + ERROR("Failed to get dir of %s", fname); + goto out; + } + + if (lcr_util_generate_random_str(random_tmp, (size_t)RANDOM_TMP_PATH)) { + ERROR("Failed to generate random str for random path"); + goto out; + } + + nret = snprintf(rpath, PATH_MAX, ".tmp-%s-%s", base, random_tmp); + if (nret < 0 || (size_t)nret >= PATH_MAX) { + ERROR("Failed to generate tmp base file"); + goto out; + } + + result = lcr_util_path_join(dir, rpath); + +out: + free(base); + free(dir); + return result; +} + +static int do_atomic_write_file(const char *fname, const char *content, size_t content_len, mode_t mode, bool sync) +{ + int ret = 0; + int dst_fd = -1; + ssize_t len = 0; + + dst_fd = lcr_util_open(fname, O_WRONLY | O_CREAT | O_TRUNC, mode); + if (dst_fd < 0) { + SYSERROR("Creat file: %s, failed", fname); + ret = -1; + goto free_out; + } + + len = lcr_util_write_nointr(dst_fd, content, content_len); + if (len < 0 || ((size_t)len) != content_len) { + ret = -1; + SYSERROR("Write file failed"); + goto free_out; + } + + if (sync && (fdatasync(dst_fd) != 0)) { + ret = -1; + SYSERROR("Failed to sync data of file:%s", fname); + goto free_out; + } + +free_out: + if (dst_fd >= 0) { + close(dst_fd); + } + return ret; +} + +int lcr_util_atomic_write_file(const char *fname, const char *content, size_t content_len, mode_t mode, bool sync) +{ + int ret = 0; + char *tmp_file = NULL; + char rpath[PATH_MAX] = { 0x00 }; + + if (fname == NULL) { + return -1; + } + if (content == NULL || content_len == 0) { + return 0; + } + + if (cleanpath(fname, rpath, sizeof(rpath)) == NULL) { + return -1; + } + + tmp_file = lcr_util_get_random_tmp_file(fname); + if (tmp_file == NULL) { + ERROR("Failed to get tmp file for %s", fname); + return -1; + } + + ret = do_atomic_write_file(tmp_file, content, content_len, mode, sync); + if (ret != 0) { + ERROR("Failed to write content to tmp file for %s", tmp_file); + ret = -1; + goto free_out; + } + + ret = rename(tmp_file, rpath); + if (ret != 0) { + ERROR("Failed to rename old file %s to target %s", tmp_file, rpath); + ret = -1; + goto free_out; + } + +free_out: + if (ret != 0 && unlink(tmp_file) != 0 && errno != ENOENT) { + SYSERROR("Failed to remove temp file:%s", tmp_file); + } + free(tmp_file); + return ret; +} + /* swap in oci is memoy+swap, so here we need to get real swap */ int lcr_util_get_real_swap(int64_t memory, int64_t memory_swap, int64_t *swap) { diff --git a/src/utils.h b/src/utils.h index 6a3764b..51e0dea 100644 --- a/src/utils.h +++ b/src/utils.h @@ -215,7 +215,11 @@ int lcr_util_safe_llong(const char *numstr, long long *converted); char *lcr_util_strdup_s(const char *src); int lcr_util_null_stdfds(void); -int lcr_util_atomic_write_file(const char *filepath, const char *content); +int lcr_util_flock_append_file(const char *filepath, const char *content); + +char *lcr_util_get_random_tmp_file(const char *fname); + +int lcr_util_atomic_write_file(const char *fname, const char *content, size_t content_len, mode_t mode, bool sync); int lcr_util_get_real_swap(int64_t memory, int64_t memory_swap, int64_t *swap); int lcr_util_trans_cpushare_to_cpuweight(int64_t cpu_share); -- 2.34.1