1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "alloc-util.h"
6 #include "blockdev-util.h"
7 #include "chase-symlinks.h"
8 #include "conf-parser.h"
9 #include "dirent-util.h"
11 #include "glyph-util.h"
13 #include "hexdecoct.h"
14 #include "install-file.h"
15 #include "parse-helpers.h"
16 #include "parse-util.h"
17 #include "process-util.h"
19 #include "specifier.h"
20 #include "stat-util.h"
21 #include "stdio-util.h"
23 #include "sync-util.h"
24 #include "sysupdate-pattern.h"
25 #include "sysupdate-resource.h"
26 #include "sysupdate-transfer.h"
27 #include "sysupdate-util.h"
28 #include "sysupdate.h"
29 #include "tmpfile-util.h"
32 /* Default value for InstancesMax= for fs object targets */
33 #define DEFAULT_FILE_INSTANCES_MAX 3
35 Transfer
*transfer_free(Transfer
*t
) {
39 t
->temporary_path
= rm_rf_subvolume_and_free(t
->temporary_path
);
41 free(t
->definition_path
);
43 strv_free(t
->protected_versions
);
44 free(t
->current_symlink
);
47 partition_info_destroy(&t
->partition_info
);
49 resource_destroy(&t
->source
);
50 resource_destroy(&t
->target
);
55 Transfer
*transfer_new(void) {
63 .source
.type
= _RESOURCE_TYPE_INVALID
,
64 .target
.type
= _RESOURCE_TYPE_INVALID
,
65 .remove_temporary
= true,
67 .tries_left
= UINT64_MAX
,
68 .tries_done
= UINT64_MAX
,
71 /* the three flags, as configured by the user */
76 /* the read only flag, as ultimately determined */
77 .install_read_only
= -1,
79 .partition_info
= PARTITION_INFO_NULL
,
85 static const Specifier specifier_table
[] = {
86 COMMON_SYSTEM_SPECIFIERS
,
87 COMMON_TMP_SPECIFIERS
,
91 static int config_parse_protect_version(
96 unsigned section_line
,
103 _cleanup_free_
char *resolved
= NULL
;
104 char ***protected_versions
= ASSERT_PTR(data
);
109 r
= specifier_printf(rvalue
, NAME_MAX
, specifier_table
, arg_root
, NULL
, &resolved
);
111 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
112 "Failed to expand specifiers in ProtectVersion=, ignoring: %s", rvalue
);
116 if (!version_is_valid(resolved
)) {
117 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
118 "ProtectVersion= string is not valid, ignoring: %s", resolved
);
122 r
= strv_extend(protected_versions
, resolved
);
129 static int config_parse_min_version(
131 const char *filename
,
134 unsigned section_line
,
141 _cleanup_free_
char *resolved
= NULL
;
142 char **version
= ASSERT_PTR(data
);
147 r
= specifier_printf(rvalue
, NAME_MAX
, specifier_table
, arg_root
, NULL
, &resolved
);
149 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
150 "Failed to expand specifiers in MinVersion=, ignoring: %s", rvalue
);
154 if (!version_is_valid(rvalue
)) {
155 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
156 "MinVersion= string is not valid, ignoring: %s", resolved
);
160 return free_and_replace(*version
, resolved
);
163 static int config_parse_current_symlink(
165 const char *filename
,
168 unsigned section_line
,
175 _cleanup_free_
char *resolved
= NULL
;
176 char **current_symlink
= ASSERT_PTR(data
);
181 r
= specifier_printf(rvalue
, NAME_MAX
, specifier_table
, arg_root
, NULL
, &resolved
);
183 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
184 "Failed to expand specifiers in CurrentSymlink=, ignoring: %s", rvalue
);
188 r
= path_simplify_and_warn(resolved
, 0, unit
, filename
, line
, lvalue
);
192 return free_and_replace(*current_symlink
, resolved
);
195 static int config_parse_instances_max(
197 const char *filename
,
200 unsigned section_line
,
207 uint64_t *instances_max
= data
, i
;
213 if (isempty(rvalue
)) {
214 *instances_max
= 0; /* Revert to default logic, see transfer_read_definition() */
218 r
= safe_atou64(rvalue
, &i
);
220 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
221 "Failed to parse InstancesMax= value, ignoring: %s", rvalue
);
226 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
227 "InstancesMax= value must be at least 2, bumping: %s", rvalue
);
235 static int config_parse_resource_pattern(
237 const char *filename
,
240 unsigned section_line
,
247 char ***patterns
= ASSERT_PTR(data
);
252 if (isempty(rvalue
)) {
253 *patterns
= strv_free(*patterns
);
258 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
;
260 r
= extract_first_word(&rvalue
, &word
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_UNESCAPE_RELAX
);
262 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
263 "Failed to extract first pattern from MatchPattern=, ignoring: %s", rvalue
);
269 r
= specifier_printf(word
, NAME_MAX
, specifier_table
, arg_root
, NULL
, &resolved
);
271 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
272 "Failed to expand specifiers in MatchPattern=, ignoring: %s", rvalue
);
276 if (!pattern_valid(resolved
))
277 return log_syntax(unit
, LOG_ERR
, filename
, line
, SYNTHETIC_ERRNO(EINVAL
),
278 "MatchPattern= string is not valid, refusing: %s", resolved
);
280 r
= strv_consume(patterns
, TAKE_PTR(resolved
));
285 strv_uniq(*patterns
);
289 static int config_parse_resource_path(
291 const char *filename
,
294 unsigned section_line
,
301 _cleanup_free_
char *resolved
= NULL
;
302 Resource
*rr
= ASSERT_PTR(data
);
307 if (streq(rvalue
, "auto")) {
308 rr
->path_auto
= true;
309 rr
->path
= mfree(rr
->path
);
313 r
= specifier_printf(rvalue
, PATH_MAX
-1, specifier_table
, arg_root
, NULL
, &resolved
);
315 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
316 "Failed to expand specifiers in Path=, ignoring: %s", rvalue
);
320 /* Note that we don't validate the path as being absolute or normalized. We'll do that in
321 * transfer_read_definition() as we might not know yet whether Path refers to an URL or a file system
324 rr
->path_auto
= false;
325 return free_and_replace(rr
->path
, resolved
);
328 static DEFINE_CONFIG_PARSE_ENUM(config_parse_resource_type
, resource_type
, ResourceType
, "Invalid resource type");
330 static int config_parse_resource_ptype(
332 const char *filename
,
335 unsigned section_line
,
342 Resource
*rr
= ASSERT_PTR(data
);
347 r
= gpt_partition_type_uuid_from_string(rvalue
, &rr
->partition_type
);
349 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
350 "Failed parse partition type, ignoring: %s", rvalue
);
354 rr
->partition_type_set
= true;
358 static int config_parse_partition_uuid(
360 const char *filename
,
363 unsigned section_line
,
370 Transfer
*t
= ASSERT_PTR(data
);
375 r
= sd_id128_from_string(rvalue
, &t
->partition_uuid
);
377 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
378 "Failed parse partition UUID, ignoring: %s", rvalue
);
382 t
->partition_uuid_set
= true;
386 static int config_parse_partition_flags(
388 const char *filename
,
391 unsigned section_line
,
398 Transfer
*t
= ASSERT_PTR(data
);
403 r
= safe_atou64(rvalue
, &t
->partition_flags
);
405 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
406 "Failed parse partition flags, ignoring: %s", rvalue
);
410 t
->partition_flags_set
= true;
414 int transfer_read_definition(Transfer
*t
, const char *path
) {
420 ConfigTableItem table
[] = {
421 { "Transfer", "MinVersion", config_parse_min_version
, 0, &t
->min_version
},
422 { "Transfer", "ProtectVersion", config_parse_protect_version
, 0, &t
->protected_versions
},
423 { "Transfer", "Verify", config_parse_bool
, 0, &t
->verify
},
424 { "Source", "Type", config_parse_resource_type
, 0, &t
->source
.type
},
425 { "Source", "Path", config_parse_resource_path
, 0, &t
->source
},
426 { "Source", "MatchPattern", config_parse_resource_pattern
, 0, &t
->source
.patterns
},
427 { "Target", "Type", config_parse_resource_type
, 0, &t
->target
.type
},
428 { "Target", "Path", config_parse_resource_path
, 0, &t
->target
},
429 { "Target", "MatchPattern", config_parse_resource_pattern
, 0, &t
->target
.patterns
},
430 { "Target", "MatchPartitionType", config_parse_resource_ptype
, 0, &t
->target
},
431 { "Target", "PartitionUUID", config_parse_partition_uuid
, 0, t
},
432 { "Target", "PartitionFlags", config_parse_partition_flags
, 0, t
},
433 { "Target", "PartitionNoAuto", config_parse_tristate
, 0, &t
->no_auto
},
434 { "Target", "PartitionGrowFileSystem", config_parse_tristate
, 0, &t
->growfs
},
435 { "Target", "ReadOnly", config_parse_tristate
, 0, &t
->read_only
},
436 { "Target", "Mode", config_parse_mode
, 0, &t
->mode
},
437 { "Target", "TriesLeft", config_parse_uint64
, 0, &t
->tries_left
},
438 { "Target", "TriesDone", config_parse_uint64
, 0, &t
->tries_done
},
439 { "Target", "InstancesMax", config_parse_instances_max
, 0, &t
->instances_max
},
440 { "Target", "RemoveTemporary", config_parse_bool
, 0, &t
->remove_temporary
},
441 { "Target", "CurrentSymlink", config_parse_current_symlink
, 0, &t
->current_symlink
},
445 r
= config_parse(NULL
, path
, NULL
,
449 config_item_table_lookup
, table
,
456 if (!RESOURCE_IS_SOURCE(t
->source
.type
))
457 return log_syntax(NULL
, LOG_ERR
, path
, 1, SYNTHETIC_ERRNO(EINVAL
),
458 "Source Type= must be one of url-file, url-tar, tar, regular-file, directory, subvolume.");
460 if (t
->target
.type
< 0) {
461 switch (t
->source
.type
) {
463 case RESOURCE_URL_FILE
:
464 case RESOURCE_REGULAR_FILE
:
466 t
->target
.path
&& path_startswith(t
->target
.path
, "/dev/") ?
467 RESOURCE_PARTITION
: RESOURCE_REGULAR_FILE
;
470 case RESOURCE_URL_TAR
:
472 case RESOURCE_DIRECTORY
:
473 t
->target
.type
= RESOURCE_DIRECTORY
;
476 case RESOURCE_SUBVOLUME
:
477 t
->target
.type
= RESOURCE_SUBVOLUME
;
481 assert_not_reached();
485 if (!RESOURCE_IS_TARGET(t
->target
.type
))
486 return log_syntax(NULL
, LOG_ERR
, path
, 1, SYNTHETIC_ERRNO(EINVAL
),
487 "Target Type= must be one of partition, regular-file, directory, subvolume.");
489 if ((IN_SET(t
->source
.type
, RESOURCE_URL_FILE
, RESOURCE_PARTITION
, RESOURCE_REGULAR_FILE
) &&
490 !IN_SET(t
->target
.type
, RESOURCE_PARTITION
, RESOURCE_REGULAR_FILE
)) ||
491 (IN_SET(t
->source
.type
, RESOURCE_URL_TAR
, RESOURCE_TAR
, RESOURCE_DIRECTORY
, RESOURCE_SUBVOLUME
) &&
492 !IN_SET(t
->target
.type
, RESOURCE_DIRECTORY
, RESOURCE_SUBVOLUME
)))
493 return log_syntax(NULL
, LOG_ERR
, path
, 1, SYNTHETIC_ERRNO(EINVAL
),
494 "Target type '%s' is incompatible with source type '%s', refusing.",
495 resource_type_to_string(t
->source
.type
), resource_type_to_string(t
->target
.type
));
497 if (!t
->source
.path
&& !t
->source
.path_auto
)
498 return log_syntax(NULL
, LOG_ERR
, path
, 1, SYNTHETIC_ERRNO(EINVAL
),
499 "Source specification lacks Path=.");
501 if (t
->source
.path
) {
502 if (RESOURCE_IS_FILESYSTEM(t
->source
.type
) || t
->source
.type
== RESOURCE_PARTITION
)
503 if (!path_is_absolute(t
->source
.path
) || !path_is_normalized(t
->source
.path
))
504 return log_syntax(NULL
, LOG_ERR
, path
, 1, SYNTHETIC_ERRNO(EINVAL
),
505 "Source path is not a normalized, absolute path: %s", t
->source
.path
);
507 /* We unofficially support file:// in addition to http:// and https:// for url
508 * sources. That's mostly for testing, since it relieves us from having to set up a HTTP
509 * server, and CURL abstracts this away from us thankfully. */
510 if (RESOURCE_IS_URL(t
->source
.type
))
511 if (!http_url_is_valid(t
->source
.path
) && !file_url_is_valid(t
->source
.path
))
512 return log_syntax(NULL
, LOG_ERR
, path
, 1, SYNTHETIC_ERRNO(EINVAL
),
513 "Source path is not a valid HTTP or HTTPS URL: %s", t
->source
.path
);
516 if (strv_isempty(t
->source
.patterns
))
517 return log_syntax(NULL
, LOG_ERR
, path
, 1, SYNTHETIC_ERRNO(EINVAL
),
518 "Source specification lacks MatchPattern=.");
520 if (!t
->target
.path
&& !t
->target
.path_auto
)
521 return log_syntax(NULL
, LOG_ERR
, path
, 1, SYNTHETIC_ERRNO(EINVAL
),
522 "Target specification lacks Path= field.");
524 if (t
->target
.path
&&
525 (!path_is_absolute(t
->target
.path
) || !path_is_normalized(t
->target
.path
)))
526 return log_syntax(NULL
, LOG_ERR
, path
, 1, SYNTHETIC_ERRNO(EINVAL
),
527 "Target path is not a normalized, absolute path: %s", t
->target
.path
);
529 if (strv_isempty(t
->target
.patterns
)) {
530 strv_free(t
->target
.patterns
);
531 t
->target
.patterns
= strv_copy(t
->source
.patterns
);
532 if (!t
->target
.patterns
)
536 if (t
->current_symlink
&& !RESOURCE_IS_FILESYSTEM(t
->target
.type
) && !path_is_absolute(t
->current_symlink
))
537 return log_syntax(NULL
, LOG_ERR
, path
, 1, SYNTHETIC_ERRNO(EINVAL
),
538 "Current symlink must be absolute path if target is partition: %s", t
->current_symlink
);
540 /* When no instance limit is set, use all available partition slots in case of partitions, or 3 in case of fs objects */
541 if (t
->instances_max
== 0)
542 t
->instances_max
= t
->target
.type
== RESOURCE_PARTITION
? UINT64_MAX
: DEFAULT_FILE_INSTANCES_MAX
;
547 int transfer_resolve_paths(
554 /* If Path=auto is used in [Source] or [Target] sections, let's automatically detect the path of the
555 * block device to use. Moreover, if this path points to a directory but we need a block device,
556 * automatically determine the backing block device, so that users can reference block devices by
561 r
= resource_resolve_path(&t
->source
, root
, node
);
565 r
= resource_resolve_path(&t
->target
, root
, node
);
572 static void transfer_remove_temporary(Transfer
*t
) {
573 _cleanup_(closedirp
) DIR *d
= NULL
;
578 if (!t
->remove_temporary
)
581 if (!IN_SET(t
->target
.type
, RESOURCE_REGULAR_FILE
, RESOURCE_DIRECTORY
, RESOURCE_SUBVOLUME
))
584 /* Removes all temporary files/dirs from previous runs in the target directory, i.e. all those starting with '.#' */
586 d
= opendir(t
->target
.path
);
591 log_debug_errno(errno
, "Failed to open target directory '%s', ignoring: %m", t
->target
.path
);
599 de
= readdir_no_dot(d
);
602 log_debug_errno(errno
, "Failed to read target directory '%s', ignoring: %m", t
->target
.path
);
606 if (!startswith(de
->d_name
, ".#"))
609 r
= rm_rf_child(dirfd(d
), de
->d_name
, REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
|REMOVE_CHMOD
);
613 log_warning_errno(r
, "Failed to remove temporary resource instance '%s/%s', ignoring: %m", t
->target
.path
, de
->d_name
);
617 log_debug("Removed temporary resource instance '%s/%s'.", t
->target
.path
, de
->d_name
);
624 const char *extra_protected_version
) {
626 uint64_t instances_max
, limit
;
631 transfer_remove_temporary(t
);
633 /* First, calculate how many instances to keep, based on the instance limit — but keep at least one */
635 instances_max
= arg_instances_max
!= UINT64_MAX
? arg_instances_max
: t
->instances_max
;
636 assert(instances_max
>= 1);
637 if (instances_max
== UINT64_MAX
) /* Keep infinite instances? */
639 else if (space
> instances_max
)
640 return log_error_errno(SYNTHETIC_ERRNO(ENOSPC
),
641 "Asked to delete more instances than total maximum allowed number of instances, refusing.");
642 else if (space
== instances_max
)
643 return log_error_errno(SYNTHETIC_ERRNO(ENOSPC
),
644 "Asked to delete all possible instances, can't allow that. One instance must always remain.");
646 limit
= instances_max
- space
;
648 if (t
->target
.type
== RESOURCE_PARTITION
) {
651 /* If we are looking at a partition table, we also have to take into account how many
652 * partition slots of the right type are available */
654 if (t
->target
.n_empty
+ t
->target
.n_instances
< 2)
655 return log_error_errno(SYNTHETIC_ERRNO(ENOSPC
),
656 "Partition table has less than two partition slots of the right type " SD_ID128_UUID_FORMAT_STR
" (%s), refusing.",
657 SD_ID128_FORMAT_VAL(t
->target
.partition_type
),
658 gpt_partition_type_uuid_to_string(t
->target
.partition_type
));
659 if (space
> t
->target
.n_empty
+ t
->target
.n_instances
)
660 return log_error_errno(SYNTHETIC_ERRNO(ENOSPC
),
661 "Partition table does not have enough partition slots of right type " SD_ID128_UUID_FORMAT_STR
" (%s) for operation.",
662 SD_ID128_FORMAT_VAL(t
->target
.partition_type
),
663 gpt_partition_type_uuid_to_string(t
->target
.partition_type
));
664 if (space
== t
->target
.n_empty
+ t
->target
.n_instances
)
665 return log_error_errno(SYNTHETIC_ERRNO(ENOSPC
),
666 "Asked to empty all partition table slots of the right type " SD_ID128_UUID_FORMAT_STR
" (%s), can't allow that. One instance must always remain.",
667 SD_ID128_FORMAT_VAL(t
->target
.partition_type
),
668 gpt_partition_type_uuid_to_string(t
->target
.partition_type
));
670 rm
= LESS_BY(space
, t
->target
.n_empty
);
671 remain
= LESS_BY(t
->target
.n_instances
, rm
);
672 limit
= MIN(limit
, remain
);
675 while (t
->target
.n_instances
> limit
) {
677 size_t p
= t
->target
.n_instances
- 1;
680 oldest
= t
->target
.instances
[p
];
683 /* If this is listed among the protected versions, then let's not remove it */
684 if (!strv_contains(t
->protected_versions
, oldest
->metadata
.version
) &&
685 (!extra_protected_version
|| !streq(extra_protected_version
, oldest
->metadata
.version
)))
688 log_debug("Version '%s' is protected, not removing.", oldest
->metadata
.version
);
697 if (!oldest
) /* Nothing more to remove */
700 assert(oldest
->resource
);
702 log_info("%s Removing old '%s' (%s).", special_glyph(SPECIAL_GLYPH_RECYCLING
), oldest
->path
, resource_type_to_string(oldest
->resource
->type
));
704 switch (t
->target
.type
) {
706 case RESOURCE_REGULAR_FILE
:
707 case RESOURCE_DIRECTORY
:
708 case RESOURCE_SUBVOLUME
:
709 r
= rm_rf(oldest
->path
, REMOVE_ROOT
|REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
|REMOVE_MISSING_OK
|REMOVE_CHMOD
);
710 if (r
< 0 && r
!= -ENOENT
)
711 return log_error_errno(r
, "Failed to make room, deleting '%s' failed: %m", oldest
->path
);
715 case RESOURCE_PARTITION
: {
716 PartitionInfo pinfo
= oldest
->partition_info
;
718 /* label "_empty" means "no contents" for our purposes */
719 pinfo
.label
= (char*) "_empty";
721 r
= patch_partition(t
->target
.path
, &pinfo
, PARTITION_LABEL
);
730 assert_not_reached();
734 instance_free(oldest
);
735 memmove(t
->target
.instances
+ p
, t
->target
.instances
+ p
+ 1, (t
->target
.n_instances
- p
- 1) * sizeof(Instance
*));
736 t
->target
.n_instances
--;
744 static void compile_pattern_fields(
747 InstanceMetadata
*ret
) {
753 *ret
= (InstanceMetadata
) {
754 .version
= i
->metadata
.version
,
756 /* We generally prefer explicitly configured values for the transfer over those automatically
757 * derived from the source instance. Also, if the source is a tar archive, then let's not
758 * patch mtime/mode and use the one embedded in the tar file */
759 .partition_uuid
= t
->partition_uuid_set
? t
->partition_uuid
: i
->metadata
.partition_uuid
,
760 .partition_uuid_set
= t
->partition_uuid_set
|| i
->metadata
.partition_uuid_set
,
761 .partition_flags
= t
->partition_flags_set
? t
->partition_flags
: i
->metadata
.partition_flags
,
762 .partition_flags_set
= t
->partition_flags_set
|| i
->metadata
.partition_flags_set
,
763 .mtime
= RESOURCE_IS_TAR(i
->resource
->type
) ? USEC_INFINITY
: i
->metadata
.mtime
,
764 .mode
= t
->mode
!= MODE_INVALID
? t
->mode
: (RESOURCE_IS_TAR(i
->resource
->type
) ? MODE_INVALID
: i
->metadata
.mode
),
765 .size
= i
->metadata
.size
,
766 .tries_done
= t
->tries_done
!= UINT64_MAX
? t
->tries_done
:
767 i
->metadata
.tries_done
!= UINT64_MAX
? i
->metadata
.tries_done
: 0,
768 .tries_left
= t
->tries_left
!= UINT64_MAX
? t
->tries_left
:
769 i
->metadata
.tries_left
!= UINT64_MAX
? i
->metadata
.tries_left
: 3,
770 .no_auto
= t
->no_auto
>= 0 ? t
->no_auto
: i
->metadata
.no_auto
,
771 .read_only
= t
->read_only
>= 0 ? t
->read_only
: i
->metadata
.read_only
,
772 .growfs
= t
->growfs
>= 0 ? t
->growfs
: i
->metadata
.growfs
,
773 .sha256sum_set
= i
->metadata
.sha256sum_set
,
776 memcpy(ret
->sha256sum
, i
->metadata
.sha256sum
, sizeof(ret
->sha256sum
));
779 static int run_helper(
782 const char * const cmdline
[]) {
790 r
= safe_fork(name
, FORK_RESET_SIGNALS
|FORK_DEATHSIG
|FORK_LOG
|FORK_WAIT
, NULL
);
796 (void) unsetenv("NOTIFY_SOCKET");
797 execv(path
, (char *const*) cmdline
);
798 log_error_errno(errno
, "Failed to execute %s tool: %m", path
);
805 int transfer_acquire_instance(Transfer
*t
, Instance
*i
) {
806 _cleanup_free_
char *formatted_pattern
= NULL
, *digest
= NULL
;
807 char offset
[DECIMAL_STR_MAX(uint64_t)+1], max_size
[DECIMAL_STR_MAX(uint64_t)+1];
808 const char *where
= NULL
;
816 assert(t
== container_of(i
->resource
, Transfer
, source
));
818 /* Does this instance already exist in the target? Then we don't need to acquire anything */
819 existing
= resource_find_instance(&t
->target
, i
->metadata
.version
);
821 log_info("No need to acquire '%s', already installed.", i
->path
);
825 assert(!t
->final_path
);
826 assert(!t
->temporary_path
);
827 assert(!strv_isempty(t
->target
.patterns
));
829 /* Format the target name using the first pattern specified */
830 compile_pattern_fields(t
, i
, &f
);
831 r
= pattern_format(t
->target
.patterns
[0], &f
, &formatted_pattern
);
833 return log_error_errno(r
, "Failed to format target pattern: %m");
835 if (RESOURCE_IS_FILESYSTEM(t
->target
.type
)) {
837 if (!filename_is_valid(formatted_pattern
))
838 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Formatted pattern is not suitable as file name, refusing: %s", formatted_pattern
);
840 t
->final_path
= path_join(t
->target
.path
, formatted_pattern
);
844 r
= tempfn_random(t
->final_path
, "sysupdate", &t
->temporary_path
);
846 return log_error_errno(r
, "Failed to generate temporary target path: %m");
848 where
= t
->final_path
;
851 if (t
->target
.type
== RESOURCE_PARTITION
) {
852 r
= gpt_partition_label_valid(formatted_pattern
);
854 return log_error_errno(r
, "Failed to determine if formatted pattern is suitable as GPT partition label: %s", formatted_pattern
);
856 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Formatted pattern is not suitable as GPT partition label, refusing: %s", formatted_pattern
);
858 r
= find_suitable_partition(
861 t
->target
.partition_type_set
? &t
->target
.partition_type
: NULL
,
866 xsprintf(offset
, "%" PRIu64
, t
->partition_info
.start
);
867 xsprintf(max_size
, "%" PRIu64
, t
->partition_info
.size
);
869 where
= t
->partition_info
.device
;
874 log_info("%s Acquiring %s %s %s...", special_glyph(SPECIAL_GLYPH_DOWNLOAD
), i
->path
, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT
), where
);
876 if (RESOURCE_IS_URL(i
->resource
->type
)) {
877 /* For URL sources we require the SHA256 sum to be known so that we can validate the
880 if (!i
->metadata
.sha256sum_set
)
881 return log_error_errno(r
, "SHA256 checksum not known for download '%s', refusing.", i
->path
);
883 digest
= hexmem(i
->metadata
.sha256sum
, sizeof(i
->metadata
.sha256sum
));
888 switch (i
->resource
->type
) { /* Source */
890 case RESOURCE_REGULAR_FILE
:
892 switch (t
->target
.type
) { /* Target */
894 case RESOURCE_REGULAR_FILE
:
896 /* regular file → regular file (why fork off systemd-import for such a simple file
897 * copy case? implicit decompression mostly, and thus also sandboxing. Also, the
898 * importer has some tricks up its sleeve, such as sparse file generation, which we
899 * want to take benefit of, too.) */
901 r
= run_helper("(sd-import-raw)",
902 import_binary_path(),
903 (const char* const[]) {
906 "--direct", /* just copy/unpack the specified file, don't do anything else */
907 arg_sync
? "--sync=yes" : "--sync=no",
914 case RESOURCE_PARTITION
:
916 /* regular file → partition */
918 r
= run_helper("(sd-import-raw)",
919 import_binary_path(),
920 (const char* const[]) {
923 "--direct", /* just copy/unpack the specified file, don't do anything else */
925 "--size-max", max_size
,
926 arg_sync
? "--sync=yes" : "--sync=no",
934 assert_not_reached();
939 case RESOURCE_DIRECTORY
:
940 case RESOURCE_SUBVOLUME
:
941 assert(IN_SET(t
->target
.type
, RESOURCE_DIRECTORY
, RESOURCE_SUBVOLUME
));
943 /* directory/subvolume → directory/subvolume */
945 r
= run_helper("(sd-import-fs)",
946 import_fs_binary_path(),
947 (const char* const[]) {
950 "--direct", /* just untar the specified file, don't do anything else */
951 arg_sync
? "--sync=yes" : "--sync=no",
952 t
->target
.type
== RESOURCE_SUBVOLUME
? "--btrfs-subvol=yes" : "--btrfs-subvol=no",
960 assert(IN_SET(t
->target
.type
, RESOURCE_DIRECTORY
, RESOURCE_SUBVOLUME
));
962 /* tar → directory/subvolume */
964 r
= run_helper("(sd-import-tar)",
965 import_binary_path(),
966 (const char* const[]) {
969 "--direct", /* just untar the specified file, don't do anything else */
970 arg_sync
? "--sync=yes" : "--sync=no",
971 t
->target
.type
== RESOURCE_SUBVOLUME
? "--btrfs-subvol=yes" : "--btrfs-subvol=no",
978 case RESOURCE_URL_FILE
:
980 switch (t
->target
.type
) {
982 case RESOURCE_REGULAR_FILE
:
984 /* url file → regular file */
986 r
= run_helper("(sd-pull-raw)",
988 (const char* const[]) {
991 "--direct", /* just download the specified URL, don't download anything else */
992 "--verify", digest
, /* validate by explicit SHA256 sum */
993 arg_sync
? "--sync=yes" : "--sync=no",
1000 case RESOURCE_PARTITION
:
1002 /* url file → partition */
1004 r
= run_helper("(sd-pull-raw)",
1006 (const char* const[]) {
1009 "--direct", /* just download the specified URL, don't download anything else */
1010 "--verify", digest
, /* validate by explicit SHA256 sum */
1012 "--size-max", max_size
,
1013 arg_sync
? "--sync=yes" : "--sync=no",
1021 assert_not_reached();
1026 case RESOURCE_URL_TAR
:
1027 assert(IN_SET(t
->target
.type
, RESOURCE_DIRECTORY
, RESOURCE_SUBVOLUME
));
1029 r
= run_helper("(sd-pull-tar)",
1031 (const char*const[]) {
1034 "--direct", /* just download the specified URL, don't download anything else */
1035 "--verify", digest
, /* validate by explicit SHA256 sum */
1036 t
->target
.type
== RESOURCE_SUBVOLUME
? "--btrfs-subvol=yes" : "--btrfs-subvol=no",
1037 arg_sync
? "--sync=yes" : "--sync=no",
1045 assert_not_reached();
1050 if (RESOURCE_IS_FILESYSTEM(t
->target
.type
)) {
1051 bool need_sync
= false;
1052 assert(t
->temporary_path
);
1054 /* Apply file attributes if set */
1055 if (f
.mtime
!= USEC_INFINITY
) {
1058 timespec_store(&ts
, f
.mtime
);
1060 if (utimensat(AT_FDCWD
, t
->temporary_path
, (struct timespec
[2]) { ts
, ts
}, AT_SYMLINK_NOFOLLOW
) < 0)
1061 return log_error_errno(errno
, "Failed to adjust mtime of '%s': %m", t
->temporary_path
);
1066 if (f
.mode
!= MODE_INVALID
) {
1067 /* Try with AT_SYMLINK_NOFOLLOW first, because it's the safe thing to do. Older
1068 * kernels don't support that however, in that case we fall back to chmod(). Not as
1069 * safe, but shouldn't be a problem, given that we don't create symlinks here. */
1070 if (fchmodat(AT_FDCWD
, t
->temporary_path
, f
.mode
, AT_SYMLINK_NOFOLLOW
) < 0 &&
1071 (!ERRNO_IS_NOT_SUPPORTED(errno
) || chmod(t
->temporary_path
, f
.mode
) < 0))
1072 return log_error_errno(errno
, "Failed to adjust mode of '%s': %m", t
->temporary_path
);
1078 if (arg_sync
&& need_sync
) {
1079 if (t
->target
.type
== RESOURCE_REGULAR_FILE
)
1080 r
= fsync_path_and_parent_at(AT_FDCWD
, t
->temporary_path
);
1082 assert(IN_SET(t
->target
.type
, RESOURCE_DIRECTORY
, RESOURCE_SUBVOLUME
));
1083 r
= syncfs_path(AT_FDCWD
, t
->temporary_path
);
1086 return log_error_errno(r
, "Failed to synchronize file system backing '%s': %m", t
->temporary_path
);
1089 t
->install_read_only
= f
.read_only
;
1092 if (t
->target
.type
== RESOURCE_PARTITION
) {
1093 free_and_replace(t
->partition_info
.label
, formatted_pattern
);
1094 t
->partition_change
= PARTITION_LABEL
;
1096 if (f
.partition_uuid_set
) {
1097 t
->partition_info
.uuid
= f
.partition_uuid
;
1098 t
->partition_change
|= PARTITION_UUID
;
1101 if (f
.partition_flags_set
) {
1102 t
->partition_info
.flags
= f
.partition_flags
;
1103 t
->partition_change
|= PARTITION_FLAGS
;
1106 if (f
.no_auto
>= 0) {
1107 t
->partition_info
.no_auto
= f
.no_auto
;
1108 t
->partition_change
|= PARTITION_NO_AUTO
;
1111 if (f
.read_only
>= 0) {
1112 t
->partition_info
.read_only
= f
.read_only
;
1113 t
->partition_change
|= PARTITION_READ_ONLY
;
1116 if (f
.growfs
>= 0) {
1117 t
->partition_info
.growfs
= f
.growfs
;
1118 t
->partition_change
|= PARTITION_GROWFS
;
1122 /* For regular file cases the only step left is to install the file in place, which install_file()
1123 * will do via rename(). For partition cases the only step left is to update the partition table,
1124 * which is done at the same place. */
1126 log_info("Successfully acquired '%s'.", i
->path
);
1130 int transfer_install_instance(
1139 assert(i
->resource
);
1140 assert(t
== container_of(i
->resource
, Transfer
, source
));
1142 if (t
->temporary_path
) {
1143 assert(RESOURCE_IS_FILESYSTEM(t
->target
.type
));
1144 assert(t
->final_path
);
1146 r
= install_file(AT_FDCWD
, t
->temporary_path
,
1147 AT_FDCWD
, t
->final_path
,
1149 (t
->install_read_only
> 0 ? INSTALL_READ_ONLY
: 0)|
1150 (t
->target
.type
== RESOURCE_REGULAR_FILE
? INSTALL_FSYNC_FULL
: INSTALL_SYNCFS
));
1152 return log_error_errno(r
, "Failed to move '%s' into place: %m", t
->final_path
);
1154 log_info("Successfully installed '%s' (%s) as '%s' (%s).",
1156 resource_type_to_string(i
->resource
->type
),
1158 resource_type_to_string(t
->target
.type
));
1160 t
->temporary_path
= mfree(t
->temporary_path
);
1163 if (t
->partition_change
!= 0) {
1164 assert(t
->target
.type
== RESOURCE_PARTITION
);
1166 r
= patch_partition(
1169 t
->partition_change
);
1173 log_info("Successfully installed '%s' (%s) as '%s' (%s).",
1175 resource_type_to_string(i
->resource
->type
),
1176 t
->partition_info
.device
,
1177 resource_type_to_string(t
->target
.type
));
1180 if (t
->current_symlink
) {
1181 _cleanup_free_
char *buf
= NULL
, *parent
= NULL
, *relative
= NULL
, *resolved
= NULL
;
1182 const char *link_path
, *link_target
;
1183 bool resolve_link_path
= false;
1185 if (RESOURCE_IS_FILESYSTEM(t
->target
.type
)) {
1187 assert(t
->target
.path
);
1189 if (path_is_absolute(t
->current_symlink
)) {
1190 link_path
= t
->current_symlink
;
1191 resolve_link_path
= true;
1193 buf
= path_make_absolute(t
->current_symlink
, t
->target
.path
);
1200 link_target
= t
->final_path
;
1202 } else if (t
->target
.type
== RESOURCE_PARTITION
) {
1204 assert(path_is_absolute(t
->current_symlink
));
1206 link_path
= t
->current_symlink
;
1207 link_target
= t
->partition_info
.device
;
1209 resolve_link_path
= true;
1211 assert_not_reached();
1213 if (resolve_link_path
&& root
) {
1214 r
= chase_symlinks(link_path
, root
, CHASE_PREFIX_ROOT
|CHASE_NONEXISTENT
, &resolved
, NULL
);
1216 return log_error_errno(r
, "Failed to resolve current symlink path '%s': %m", link_path
);
1218 link_path
= resolved
;
1222 r
= path_extract_directory(link_path
, &parent
);
1224 return log_error_errno(r
, "Failed to extract directory of target path '%s': %m", link_path
);
1226 r
= path_make_relative(parent
, link_target
, &relative
);
1228 return log_error_errno(r
, "Failed to make symlink path '%s' relative to '%s': %m", link_target
, parent
);
1230 r
= symlink_atomic(relative
, link_path
);
1232 return log_error_errno(r
, "Failed to update current symlink '%s' %s '%s': %m",
1234 special_glyph(SPECIAL_GLYPH_ARROW_RIGHT
),
1237 log_info("Updated symlink '%s' %s '%s'.",
1238 link_path
, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT
), relative
);