From: Alberto Leiva Popper Date: Fri, 6 Oct 2023 21:47:21 +0000 (-0600) Subject: Fix rsync target location X-Git-Tag: 1.6.0~42 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=03988d6a36f64f3539a44efa411845e7b682250f;p=thirdparty%2FFORT-validator.git Fix rsync target location Was downloading rsync://a.b/c into rsync://a.b/c/c, because of an rsync complication (from rsync(1)): > A trailing slash on the source changes this behavior to avoid creating > an additional directory level at the destination. You can think of a > trailing / on a source as meaning "copy the contents of this > directory" as opposed to "copy the directory by name", but in both > cases the attributes of the containing directory are transferred to > the containing directory on the destination. In other words, each of > the following commands copies the files in the same way, including > their setting of the attributes of /dest/foo: > > rsync -av /src/foo /dest > rsync -av /src/foo/ /dest/foo --- diff --git a/src/cache/local_cache.c b/src/cache/local_cache.c index c8d45c8a..e40e4d5c 100644 --- a/src/cache/local_cache.c +++ b/src/cache/local_cache.c @@ -324,10 +324,10 @@ cache_prepare(void) if (rsync == NULL) load_metadata_json(); - error = pb_init_cache(&pb, "tmp/a"); + error = pb_init_cache(&pb, "tmp"); if (error) return error; - error = create_dir_recursive(pb.string); + error = create_dir_recursive(pb.string, true); pb_cleanup(&pb); return error; } diff --git a/src/common.c b/src/common.c index 5498b7b6..f09cf321 100644 --- a/src/common.c +++ b/src/common.c @@ -224,41 +224,31 @@ valid_file_or_dir(char const *location, bool check_file, bool check_dir, return result; } +/* + * > 0: exists + * = 0: !exists + * < 0: error + */ static int -dir_exists(char *path, bool *result) +dir_exists(char const *path) { - struct stat _stat; - char *last_slash; + struct stat meta; int error; - last_slash = strrchr(path, '/'); - if (last_slash == NULL) { - /* - * Simply because create_dir_recursive() has nothing meaningful - * to do when this happens. It's a pretty strange error. - */ - *result = true; - return 0; - } - - *last_slash = '\0'; - - if (stat(path, &_stat) == 0) { - if (!S_ISDIR(_stat.st_mode)) { - return pr_op_err_st("Path '%s' exists and is not a directory.", - path); - } - *result = true; - } else if (errno == ENOENT) { - *result = false; - } else { + if (stat(path, &meta) != 0) { error = errno; + if (error == ENOENT) + return false; pr_op_err_st("stat() failed: %s", strerror(error)); return error; } - *last_slash = '/'; - return 0; + if (!S_ISDIR(meta.st_mode)) { + return pr_op_err_st("Path '%s' exists and is not a directory.", + path); + } + + return 1; } static int @@ -283,32 +273,38 @@ create_dir(char const *path) * This function fixes that. */ int -create_dir_recursive(char const *path) +create_dir_recursive(char const *path, bool include_basename) { - char *localuri; - int i, error; - bool exist; + char *localuri, *last_slash; + int i, result = 0; localuri = pstrdup(path); /* Remove const */ - exist = false; - error = dir_exists(localuri, &exist); - if (error || exist) + if (!include_basename) { + last_slash = strrchr(localuri, '/'); + if (last_slash == NULL) + goto end; + *last_slash = '\0'; + } + + result = dir_exists(localuri); /* short circuit */ + if (result != 0) goto end; for (i = 1; localuri[i] != '\0'; i++) { if (localuri[i] == '/') { localuri[i] = '\0'; - error = create_dir(localuri); + result = create_dir(localuri); localuri[i] = '/'; - if (error) + if (result != 0) goto end; /* error msg already printed */ } } + result = create_dir(localuri); end: free(localuri); - return error; + return result; } static int diff --git a/src/common.h b/src/common.h index b2012b09..d33edbea 100644 --- a/src/common.h +++ b/src/common.h @@ -65,7 +65,7 @@ int process_file_or_dir(char const *, char const *, bool, process_file_cb, typedef int (*pr_errno_cb)(const char *, ...); bool valid_file_or_dir(char const *, bool, bool, pr_errno_cb); -int create_dir_recursive(char const *); +int create_dir_recursive(char const *, bool); int delete_dir_recursive_bottom_up(char const *); int get_current_time(time_t *); diff --git a/src/http/http.c b/src/http/http.c index 131c48b8..f193d368 100644 --- a/src/http/http.c +++ b/src/http/http.c @@ -432,7 +432,7 @@ http_download(struct rpki_uri *uri, bool *changed) if (error || !(*changed)) goto end; - error = create_dir_recursive(final_file_name); + error = create_dir_recursive(final_file_name, false); if (error) { remove(tmp_file_name); goto end; diff --git a/src/rrdp/rrdp_parser.c b/src/rrdp/rrdp_parser.c index e813359e..721b7d93 100644 --- a/src/rrdp/rrdp_parser.c +++ b/src/rrdp/rrdp_parser.c @@ -489,7 +489,7 @@ write_from_uri(char const *location, unsigned char *content, size_t content_len) /* pr_val_debug("Expanding %s.", uri_get_local(uri)); */ - error = create_dir_recursive(uri_get_local(uri)); + error = create_dir_recursive(uri_get_local(uri), false); if (error) { uri_refput(uri); return error; diff --git a/src/rsync/rsync.c b/src/rsync/rsync.c index 11d91dab..e7564a6a 100644 --- a/src/rsync/rsync.c +++ b/src/rsync/rsync.c @@ -43,6 +43,53 @@ release_args(char **args, unsigned int size) free(args); } +/* + * See rsync(1): + * + * > You can think of a trailing / on a source as meaning "copy the contents of + * > this directory" as opposed to "copy the directory by name" + * + * This gets in our way: + * + * - If URI is "rsync://a.b/d", we need to rsync into "cache/rsync/a.b" + * (rsync will create d). + * - If URI is "rsync://a.b/d/", we need to rsync into "cache/rsync/a.b/d" + * (rsync will not create d). + */ +static bool +has_trailing_slash(struct rpki_uri *uri) +{ + char const *guri; + size_t glen; + + guri = uri_get_global(uri); + glen = uri_get_global_len(uri); + + if (glen == 0) + pr_crit("URI length is zero: %s", guri); + + return guri[glen - 1] == '/'; +} + +static char * +get_target(struct rpki_uri *uri) +{ + char *target; + char *last_slash; + + target = pstrdup(uri_get_local(uri)); + + if (has_trailing_slash(uri)) + return target; + + last_slash = strrchr(target, '/'); + if (last_slash == NULL) + pr_crit("path contains zero slashes: %s", target); + + *last_slash = '\0'; + return target; +} + static void prepare_rsync(struct rpki_uri *uri, char ***args, size_t *args_len) { @@ -68,7 +115,7 @@ prepare_rsync(struct rpki_uri *uri, char ***args, size_t *args_len) if (strcmp(config_args->array[i], "$REMOTE") == 0) copy_args[i + 1] = pstrdup(uri_get_global(uri)); else if (strcmp(config_args->array[i], "$LOCAL") == 0) - copy_args[i + 1] = pstrdup(uri_get_local(uri)); + copy_args[i + 1] = pstrdup(get_target(uri)); else copy_args[i + 1] = pstrdup(config_args->array[i]); } @@ -234,12 +281,14 @@ rsync_download(struct rpki_uri *uri) pr_val_debug(" %s", args[i]); } + error = create_dir_recursive(uri_get_local(uri), + has_trailing_slash(uri)); + if (error) + goto release_args; + retries = 0; do { child_status = 0; - error = create_dir_recursive(uri_get_local(uri)); - if (error) - goto release_args; error = create_pipes(fork_fds); if (error)