| File: | build-scan/../src/import/import-tar.c |
| Warning: | line 121, column 25 Potential leak of memory pointed to by 'i' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ | |||
| 2 | ||||
| 3 | #include <linux1/fs.h> | |||
| 4 | ||||
| 5 | #include "sd-daemon.h" | |||
| 6 | #include "sd-event.h" | |||
| 7 | ||||
| 8 | #include "alloc-util.h" | |||
| 9 | #include "btrfs-util.h" | |||
| 10 | #include "copy.h" | |||
| 11 | #include "fd-util.h" | |||
| 12 | #include "fileio.h" | |||
| 13 | #include "fs-util.h" | |||
| 14 | #include "hostname-util.h" | |||
| 15 | #include "import-common.h" | |||
| 16 | #include "import-compress.h" | |||
| 17 | #include "import-tar.h" | |||
| 18 | #include "io-util.h" | |||
| 19 | #include "machine-pool.h" | |||
| 20 | #include "mkdir.h" | |||
| 21 | #include "path-util.h" | |||
| 22 | #include "process-util.h" | |||
| 23 | #include "qcow2-util.h" | |||
| 24 | #include "ratelimit.h" | |||
| 25 | #include "rm-rf.h" | |||
| 26 | #include "string-util.h" | |||
| 27 | #include "util.h" | |||
| 28 | ||||
| 29 | struct TarImport { | |||
| 30 | sd_event *event; | |||
| 31 | ||||
| 32 | char *image_root; | |||
| 33 | ||||
| 34 | TarImportFinished on_finished; | |||
| 35 | void *userdata; | |||
| 36 | ||||
| 37 | char *local; | |||
| 38 | bool_Bool force_local; | |||
| 39 | bool_Bool read_only; | |||
| 40 | bool_Bool grow_machine_directory; | |||
| 41 | ||||
| 42 | char *temp_path; | |||
| 43 | char *final_path; | |||
| 44 | ||||
| 45 | int input_fd; | |||
| 46 | int tar_fd; | |||
| 47 | ||||
| 48 | ImportCompress compress; | |||
| 49 | ||||
| 50 | uint64_t written_since_last_grow; | |||
| 51 | ||||
| 52 | sd_event_source *input_event_source; | |||
| 53 | ||||
| 54 | uint8_t buffer[16*1024]; | |||
| 55 | size_t buffer_size; | |||
| 56 | ||||
| 57 | uint64_t written_compressed; | |||
| 58 | uint64_t written_uncompressed; | |||
| 59 | ||||
| 60 | struct stat st; | |||
| 61 | ||||
| 62 | pid_t tar_pid; | |||
| 63 | ||||
| 64 | unsigned last_percent; | |||
| 65 | RateLimit progress_rate_limit; | |||
| 66 | }; | |||
| 67 | ||||
| 68 | TarImport* tar_import_unref(TarImport *i) { | |||
| 69 | if (!i) | |||
| 70 | return NULL((void*)0); | |||
| 71 | ||||
| 72 | sd_event_source_unref(i->input_event_source); | |||
| 73 | ||||
| 74 | if (i->tar_pid > 1) { | |||
| 75 | (void) kill_and_sigcont(i->tar_pid, SIGKILL9); | |||
| 76 | (void) wait_for_terminate(i->tar_pid, NULL((void*)0)); | |||
| 77 | } | |||
| 78 | ||||
| 79 | if (i->temp_path) { | |||
| 80 | (void) rm_rf(i->temp_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); | |||
| 81 | free(i->temp_path); | |||
| 82 | } | |||
| 83 | ||||
| 84 | import_compress_free(&i->compress); | |||
| 85 | ||||
| 86 | sd_event_unref(i->event); | |||
| 87 | ||||
| 88 | safe_close(i->tar_fd); | |||
| 89 | ||||
| 90 | free(i->final_path); | |||
| 91 | free(i->image_root); | |||
| 92 | free(i->local); | |||
| 93 | return mfree(i); | |||
| 94 | } | |||
| 95 | ||||
| 96 | int tar_import_new( | |||
| 97 | TarImport **ret, | |||
| 98 | sd_event *event, | |||
| 99 | const char *image_root, | |||
| 100 | TarImportFinished on_finished, | |||
| 101 | void *userdata) { | |||
| 102 | ||||
| 103 | _cleanup_(tar_import_unrefp)__attribute__((cleanup(tar_import_unrefp))) TarImport *i = NULL((void*)0); | |||
| 104 | int r; | |||
| 105 | ||||
| 106 | assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ret"), "../src/import/import-tar.c", 106 , __PRETTY_FUNCTION__); } while (0); | |||
| ||||
| 107 | ||||
| 108 | i = new0(TarImport, 1)((TarImport*) calloc((1), sizeof(TarImport))); | |||
| 109 | if (!i) | |||
| 110 | return -ENOMEM12; | |||
| 111 | ||||
| 112 | i->input_fd = i->tar_fd = -1; | |||
| 113 | i->on_finished = on_finished; | |||
| 114 | i->userdata = userdata; | |||
| 115 | ||||
| 116 | RATELIMIT_INIT(i->progress_rate_limit, 100 * USEC_PER_MSEC, 1)do { RateLimit *_r = &(i->progress_rate_limit); _r-> interval = (100 * ((usec_t) 1000ULL)); _r->burst = (1); _r ->num = 0; _r->begin = 0; } while (0); | |||
| 117 | i->last_percent = (unsigned) -1; | |||
| 118 | ||||
| 119 | i->image_root = strdup(image_root ?: "/var/lib/machines"); | |||
| 120 | if (!i->image_root) | |||
| 121 | return -ENOMEM12; | |||
| ||||
| 122 | ||||
| 123 | i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines"); | |||
| 124 | ||||
| 125 | if (event) | |||
| 126 | i->event = sd_event_ref(event); | |||
| 127 | else { | |||
| 128 | r = sd_event_default(&i->event); | |||
| 129 | if (r < 0) | |||
| 130 | return r; | |||
| 131 | } | |||
| 132 | ||||
| 133 | *ret = TAKE_PTR(i)({ typeof(i) _ptr_ = (i); (i) = ((void*)0); _ptr_; }); | |||
| 134 | ||||
| 135 | return 0; | |||
| 136 | } | |||
| 137 | ||||
| 138 | static void tar_import_report_progress(TarImport *i) { | |||
| 139 | unsigned percent; | |||
| 140 | assert(i)do { if ((__builtin_expect(!!(!(i)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("i"), "../src/import/import-tar.c", 140, __PRETTY_FUNCTION__); } while (0); | |||
| 141 | ||||
| 142 | /* We have no size information, unless the source is a regular file */ | |||
| 143 | if (!S_ISREG(i->st.st_mode)((((i->st.st_mode)) & 0170000) == (0100000))) | |||
| 144 | return; | |||
| 145 | ||||
| 146 | if (i->written_compressed >= (uint64_t) i->st.st_size) | |||
| 147 | percent = 100; | |||
| 148 | else | |||
| 149 | percent = (unsigned) ((i->written_compressed * UINT64_C(100)100UL) / (uint64_t) i->st.st_size); | |||
| 150 | ||||
| 151 | if (percent == i->last_percent) | |||
| 152 | return; | |||
| 153 | ||||
| 154 | if (!ratelimit_below(&i->progress_rate_limit)) | |||
| 155 | return; | |||
| 156 | ||||
| 157 | sd_notifyf(false0, "X_IMPORT_PROGRESS=%u", percent); | |||
| 158 | log_info("Imported %u%%.", percent)({ int _level = (((6))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/import/import-tar.c", 158, __func__, "Imported %u%%." , percent) : -abs(_e); }); | |||
| 159 | ||||
| 160 | i->last_percent = percent; | |||
| 161 | } | |||
| 162 | ||||
| 163 | static int tar_import_finish(TarImport *i) { | |||
| 164 | int r; | |||
| 165 | ||||
| 166 | assert(i)do { if ((__builtin_expect(!!(!(i)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("i"), "../src/import/import-tar.c", 166, __PRETTY_FUNCTION__); } while (0); | |||
| 167 | assert(i->tar_fd >= 0)do { if ((__builtin_expect(!!(!(i->tar_fd >= 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("i->tar_fd >= 0"), "../src/import/import-tar.c" , 167, __PRETTY_FUNCTION__); } while (0); | |||
| 168 | assert(i->temp_path)do { if ((__builtin_expect(!!(!(i->temp_path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("i->temp_path"), "../src/import/import-tar.c" , 168, __PRETTY_FUNCTION__); } while (0); | |||
| 169 | assert(i->final_path)do { if ((__builtin_expect(!!(!(i->final_path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("i->final_path"), "../src/import/import-tar.c" , 169, __PRETTY_FUNCTION__); } while (0); | |||
| 170 | ||||
| 171 | i->tar_fd = safe_close(i->tar_fd); | |||
| 172 | ||||
| 173 | if (i->tar_pid > 0) { | |||
| 174 | r = wait_for_terminate_and_check("tar", i->tar_pid, WAIT_LOG); | |||
| 175 | i->tar_pid = 0; | |||
| 176 | if (r < 0) | |||
| 177 | return r; | |||
| 178 | } | |||
| 179 | ||||
| 180 | if (i->read_only) { | |||
| 181 | r = import_make_read_only(i->temp_path); | |||
| 182 | if (r < 0) | |||
| 183 | return r; | |||
| 184 | } | |||
| 185 | ||||
| 186 | if (i->force_local) | |||
| 187 | (void) rm_rf(i->final_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); | |||
| 188 | ||||
| 189 | r = rename_noreplace(AT_FDCWD-100, i->temp_path, AT_FDCWD-100, i->final_path); | |||
| 190 | if (r < 0) | |||
| 191 | return log_error_errno(r, "Failed to move image into place: %m")({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/import/import-tar.c", 191, __func__, "Failed to move image into place: %m" ) : -abs(_e); }); | |||
| 192 | ||||
| 193 | i->temp_path = mfree(i->temp_path); | |||
| 194 | ||||
| 195 | return 0; | |||
| 196 | } | |||
| 197 | ||||
| 198 | static int tar_import_fork_tar(TarImport *i) { | |||
| 199 | int r; | |||
| 200 | ||||
| 201 | assert(i)do { if ((__builtin_expect(!!(!(i)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("i"), "../src/import/import-tar.c", 201, __PRETTY_FUNCTION__); } while (0); | |||
| 202 | ||||
| 203 | assert(!i->final_path)do { if ((__builtin_expect(!!(!(!i->final_path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("!i->final_path"), "../src/import/import-tar.c" , 203, __PRETTY_FUNCTION__); } while (0); | |||
| 204 | assert(!i->temp_path)do { if ((__builtin_expect(!!(!(!i->temp_path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("!i->temp_path"), "../src/import/import-tar.c" , 204, __PRETTY_FUNCTION__); } while (0); | |||
| 205 | assert(i->tar_fd < 0)do { if ((__builtin_expect(!!(!(i->tar_fd < 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("i->tar_fd < 0"), "../src/import/import-tar.c" , 205, __PRETTY_FUNCTION__); } while (0); | |||
| 206 | ||||
| 207 | i->final_path = strjoin(i->image_root, "/", i->local)strjoin_real((i->image_root), "/", i->local, ((void*)0) ); | |||
| 208 | if (!i->final_path) | |||
| 209 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/import/import-tar.c" , 209, __func__); | |||
| 210 | ||||
| 211 | r = tempfn_random(i->final_path, NULL((void*)0), &i->temp_path); | |||
| 212 | if (r < 0) | |||
| 213 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/import/import-tar.c" , 213, __func__); | |||
| 214 | ||||
| 215 | (void) mkdir_parents_label(i->temp_path, 0700); | |||
| 216 | ||||
| 217 | r = btrfs_subvol_make(i->temp_path); | |||
| 218 | if (r == -ENOTTY25) { | |||
| 219 | if (mkdir(i->temp_path, 0755) < 0) | |||
| 220 | return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path)({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/import/import-tar.c", 220, __func__ , "Failed to create directory %s: %m", i->temp_path) : -abs (_e); }); | |||
| 221 | } else if (r < 0) | |||
| 222 | return log_error_errno(r, "Failed to create subvolume %s: %m", i->temp_path)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/import/import-tar.c", 222, __func__, "Failed to create subvolume %s: %m" , i->temp_path) : -abs(_e); }); | |||
| 223 | else | |||
| 224 | (void) import_assign_pool_quota_and_warn(i->temp_path); | |||
| 225 | ||||
| 226 | i->tar_fd = import_fork_tar_x(i->temp_path, &i->tar_pid); | |||
| 227 | if (i->tar_fd < 0) | |||
| 228 | return i->tar_fd; | |||
| 229 | ||||
| 230 | return 0; | |||
| 231 | } | |||
| 232 | ||||
| 233 | static int tar_import_write(const void *p, size_t sz, void *userdata) { | |||
| 234 | TarImport *i = userdata; | |||
| 235 | int r; | |||
| 236 | ||||
| 237 | if (i->grow_machine_directory && i->written_since_last_grow >= GROW_INTERVAL_BYTES(10UL * 1024UL * 1024UL)) { | |||
| 238 | i->written_since_last_grow = 0; | |||
| 239 | grow_machine_directory(); | |||
| 240 | } | |||
| 241 | ||||
| 242 | r = loop_write(i->tar_fd, p, sz, false0); | |||
| 243 | if (r < 0) | |||
| 244 | return r; | |||
| 245 | ||||
| 246 | i->written_uncompressed += sz; | |||
| 247 | i->written_since_last_grow += sz; | |||
| 248 | ||||
| 249 | return 0; | |||
| 250 | } | |||
| 251 | ||||
| 252 | static int tar_import_process(TarImport *i) { | |||
| 253 | ssize_t l; | |||
| 254 | int r; | |||
| 255 | ||||
| 256 | assert(i)do { if ((__builtin_expect(!!(!(i)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("i"), "../src/import/import-tar.c", 256, __PRETTY_FUNCTION__); } while (0); | |||
| 257 | assert(i->buffer_size < sizeof(i->buffer))do { if ((__builtin_expect(!!(!(i->buffer_size < sizeof (i->buffer))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD , ("i->buffer_size < sizeof(i->buffer)"), "../src/import/import-tar.c" , 257, __PRETTY_FUNCTION__); } while (0); | |||
| 258 | ||||
| 259 | l = read(i->input_fd, i->buffer + i->buffer_size, sizeof(i->buffer) - i->buffer_size); | |||
| 260 | if (l < 0) { | |||
| 261 | if (errno(*__errno_location ()) == EAGAIN11) | |||
| 262 | return 0; | |||
| 263 | ||||
| 264 | r = log_error_errno(errno, "Failed to read input file: %m")({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/import/import-tar.c", 264, __func__ , "Failed to read input file: %m") : -abs(_e); }); | |||
| 265 | goto finish; | |||
| 266 | } | |||
| 267 | if (l == 0) { | |||
| 268 | if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) { | |||
| 269 | log_error("Premature end of file.")({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/import/import-tar.c", 269, __func__, "Premature end of file." ) : -abs(_e); }); | |||
| 270 | r = -EIO5; | |||
| 271 | goto finish; | |||
| 272 | } | |||
| 273 | ||||
| 274 | r = tar_import_finish(i); | |||
| 275 | goto finish; | |||
| 276 | } | |||
| 277 | ||||
| 278 | i->buffer_size += l; | |||
| 279 | ||||
| 280 | if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) { | |||
| 281 | r = import_uncompress_detect(&i->compress, i->buffer, i->buffer_size); | |||
| 282 | if (r < 0) { | |||
| 283 | log_error_errno(r, "Failed to detect file compression: %m")({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/import/import-tar.c", 283, __func__, "Failed to detect file compression: %m" ) : -abs(_e); }); | |||
| 284 | goto finish; | |||
| 285 | } | |||
| 286 | if (r == 0) /* Need more data */ | |||
| 287 | return 0; | |||
| 288 | ||||
| 289 | r = tar_import_fork_tar(i); | |||
| 290 | if (r < 0) | |||
| 291 | goto finish; | |||
| 292 | } | |||
| 293 | ||||
| 294 | r = import_uncompress(&i->compress, i->buffer, i->buffer_size, tar_import_write, i); | |||
| 295 | if (r < 0) { | |||
| 296 | log_error_errno(r, "Failed to decode and write: %m")({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/import/import-tar.c", 296, __func__, "Failed to decode and write: %m" ) : -abs(_e); }); | |||
| 297 | goto finish; | |||
| 298 | } | |||
| 299 | ||||
| 300 | i->written_compressed += i->buffer_size; | |||
| 301 | i->buffer_size = 0; | |||
| 302 | ||||
| 303 | tar_import_report_progress(i); | |||
| 304 | ||||
| 305 | return 0; | |||
| 306 | ||||
| 307 | finish: | |||
| 308 | if (i->on_finished) | |||
| 309 | i->on_finished(i, r, i->userdata); | |||
| 310 | else | |||
| 311 | sd_event_exit(i->event, r); | |||
| 312 | ||||
| 313 | return 0; | |||
| 314 | } | |||
| 315 | ||||
| 316 | static int tar_import_on_input(sd_event_source *s, int fd, uint32_t revents, void *userdata) { | |||
| 317 | TarImport *i = userdata; | |||
| 318 | ||||
| 319 | return tar_import_process(i); | |||
| 320 | } | |||
| 321 | ||||
| 322 | static int tar_import_on_defer(sd_event_source *s, void *userdata) { | |||
| 323 | TarImport *i = userdata; | |||
| 324 | ||||
| 325 | return tar_import_process(i); | |||
| 326 | } | |||
| 327 | ||||
| 328 | int tar_import_start(TarImport *i, int fd, const char *local, bool_Bool force_local, bool_Bool read_only) { | |||
| 329 | int r; | |||
| 330 | ||||
| 331 | assert(i)do { if ((__builtin_expect(!!(!(i)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("i"), "../src/import/import-tar.c", 331, __PRETTY_FUNCTION__); } while (0); | |||
| 332 | assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/import/import-tar.c" , 332, __PRETTY_FUNCTION__); } while (0); | |||
| 333 | assert(local)do { if ((__builtin_expect(!!(!(local)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("local"), "../src/import/import-tar.c", 333 , __PRETTY_FUNCTION__); } while (0); | |||
| 334 | ||||
| 335 | if (!machine_name_is_valid(local)hostname_is_valid(local, 0)) | |||
| 336 | return -EINVAL22; | |||
| 337 | ||||
| 338 | if (i->input_fd >= 0) | |||
| 339 | return -EBUSY16; | |||
| 340 | ||||
| 341 | r = fd_nonblock(fd, true1); | |||
| 342 | if (r < 0) | |||
| 343 | return r; | |||
| 344 | ||||
| 345 | r = free_and_strdup(&i->local, local); | |||
| 346 | if (r < 0) | |||
| 347 | return r; | |||
| 348 | i->force_local = force_local; | |||
| 349 | i->read_only = read_only; | |||
| 350 | ||||
| 351 | if (fstat(fd, &i->st) < 0) | |||
| 352 | return -errno(*__errno_location ()); | |||
| 353 | ||||
| 354 | r = sd_event_add_io(i->event, &i->input_event_source, fd, EPOLLINEPOLLIN, tar_import_on_input, i); | |||
| 355 | if (r == -EPERM1) { | |||
| 356 | /* This fd does not support epoll, for example because it is a regular file. Busy read in that case */ | |||
| 357 | r = sd_event_add_defer(i->event, &i->input_event_source, tar_import_on_defer, i); | |||
| 358 | if (r < 0) | |||
| 359 | return r; | |||
| 360 | ||||
| 361 | r = sd_event_source_set_enabled(i->input_event_source, SD_EVENT_ON); | |||
| 362 | } | |||
| 363 | if (r < 0) | |||
| 364 | return r; | |||
| 365 | ||||
| 366 | i->input_fd = fd; | |||
| 367 | return r; | |||
| 368 | } |