salvage: add repository path helpers
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
#define NWSALVAGE_DEFAULT_RECYCLE_NAME ".recycle"
|
||||
#define NWSALVAGE_DEFAULT_METADATA_NAME ".salvage"
|
||||
#define NWSALVAGE_REPOSITORY_NAME_MAX 64
|
||||
#define NWSALVAGE_PATH_MAX 4096
|
||||
|
||||
struct nwsalvage_config {
|
||||
int enabled;
|
||||
@@ -24,5 +25,14 @@ int nwsalvage_config_set_repositories(struct nwsalvage_config *config,
|
||||
int nwsalvage_config_parse_repositories(struct nwsalvage_config *config,
|
||||
const char *line);
|
||||
int nwsalvage_repository_name_valid(const char *name);
|
||||
int nwsalvage_relative_path_valid(const char *relative_path);
|
||||
int nwsalvage_build_recycle_path(char *out, size_t out_len,
|
||||
const struct nwsalvage_config *config,
|
||||
const char *volume_root,
|
||||
const char *relative_path);
|
||||
int nwsalvage_build_metadata_path(char *out, size_t out_len,
|
||||
const struct nwsalvage_config *config,
|
||||
const char *volume_root,
|
||||
const char *relative_path);
|
||||
|
||||
#endif
|
||||
|
||||
124
src/nwsalvage.c
124
src/nwsalvage.c
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int nwsalvage_repository_name_valid(const char *name)
|
||||
@@ -118,3 +119,126 @@ int nwsalvage_config_parse_repositories(struct nwsalvage_config *config,
|
||||
recycle_repository,
|
||||
metadata_repository));
|
||||
}
|
||||
|
||||
static int path_is_separator(char c)
|
||||
{
|
||||
return(c == '/');
|
||||
}
|
||||
|
||||
int nwsalvage_relative_path_valid(const char *relative_path)
|
||||
{
|
||||
const char *p;
|
||||
size_t component_len = 0;
|
||||
int component_is_dot = 0;
|
||||
int component_is_dotdot = 0;
|
||||
|
||||
if (!relative_path || !*relative_path) {
|
||||
errno = EINVAL;
|
||||
return(0);
|
||||
}
|
||||
|
||||
if (path_is_separator(relative_path[0])) {
|
||||
errno = EINVAL;
|
||||
return(0);
|
||||
}
|
||||
|
||||
p = relative_path;
|
||||
while (1) {
|
||||
char c = *p;
|
||||
|
||||
if (!c || path_is_separator(c)) {
|
||||
if (!component_len || component_is_dot || component_is_dotdot) {
|
||||
errno = EINVAL;
|
||||
return(0);
|
||||
}
|
||||
if (!c)
|
||||
break;
|
||||
component_len = 0;
|
||||
component_is_dot = 0;
|
||||
component_is_dotdot = 0;
|
||||
p++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == ':' || c == '\\') {
|
||||
errno = EINVAL;
|
||||
return(0);
|
||||
}
|
||||
|
||||
if (!component_len) {
|
||||
component_is_dot = (c == '.');
|
||||
component_is_dotdot = (c == '.');
|
||||
} else if (component_len == 1 && component_is_dot && c == '.') {
|
||||
component_is_dot = 0;
|
||||
component_is_dotdot = 1;
|
||||
} else {
|
||||
component_is_dot = 0;
|
||||
component_is_dotdot = 0;
|
||||
}
|
||||
|
||||
component_len++;
|
||||
p++;
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
static int build_path(char *out, size_t out_len,
|
||||
const char *volume_root,
|
||||
const char *repository,
|
||||
const char *relative_path,
|
||||
const char *suffix)
|
||||
{
|
||||
int n;
|
||||
const char *separator = "/";
|
||||
size_t volume_root_len;
|
||||
|
||||
if (!out || !out_len || !volume_root || !*volume_root ||
|
||||
!nwsalvage_repository_name_valid(repository) ||
|
||||
!nwsalvage_relative_path_valid(relative_path)) {
|
||||
if (!errno) errno = EINVAL;
|
||||
return(-1);
|
||||
}
|
||||
|
||||
volume_root_len = strlen(volume_root);
|
||||
if (volume_root_len && path_is_separator(volume_root[volume_root_len - 1]))
|
||||
separator = "";
|
||||
|
||||
n = snprintf(out, out_len, "%s%s%s/%s%s",
|
||||
volume_root, separator, repository, relative_path,
|
||||
suffix ? suffix : "");
|
||||
if (n < 0 || (size_t)n >= out_len) {
|
||||
errno = ENAMETOOLONG;
|
||||
return(-1);
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
int nwsalvage_build_recycle_path(char *out, size_t out_len,
|
||||
const struct nwsalvage_config *config,
|
||||
const char *volume_root,
|
||||
const char *relative_path)
|
||||
{
|
||||
if (!config) {
|
||||
errno = EINVAL;
|
||||
return(-1);
|
||||
}
|
||||
|
||||
return(build_path(out, out_len, volume_root,
|
||||
config->recycle_repository, relative_path, NULL));
|
||||
}
|
||||
|
||||
int nwsalvage_build_metadata_path(char *out, size_t out_len,
|
||||
const struct nwsalvage_config *config,
|
||||
const char *volume_root,
|
||||
const char *relative_path)
|
||||
{
|
||||
if (!config) {
|
||||
errno = EINVAL;
|
||||
return(-1);
|
||||
}
|
||||
|
||||
return(build_path(out, out_len, volume_root,
|
||||
config->metadata_repository, relative_path, ".json"));
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ static int expect_true(int condition, const char *message)
|
||||
int main(void)
|
||||
{
|
||||
struct nwsalvage_config config;
|
||||
char path[NWSALVAGE_PATH_MAX];
|
||||
int failures = 0;
|
||||
|
||||
failures += expect_true(nwsalvage_config_defaults(&config) == 0,
|
||||
@@ -53,6 +54,40 @@ int main(void)
|
||||
&config, ".recycle .salvage extra") < 0,
|
||||
"repository line rejects extra tokens");
|
||||
|
||||
failures += expect_true(nwsalvage_config_defaults(&config) == 0,
|
||||
"defaults reset after custom repository parse");
|
||||
failures += expect_true(nwsalvage_build_recycle_path(
|
||||
path, sizeof(path), &config, "/srv/nw/SYS",
|
||||
"SUPERVISOR/PUBLIC/PMDFLTS.INI") == 0,
|
||||
"recycle path builds");
|
||||
failures += expect_true(!strcmp(path,
|
||||
"/srv/nw/SYS/.recycle/SUPERVISOR/PUBLIC/PMDFLTS.INI"),
|
||||
"recycle path layout");
|
||||
failures += expect_true(nwsalvage_build_metadata_path(
|
||||
path, sizeof(path), &config, "/srv/nw/SYS/",
|
||||
"SUPERVISOR/PUBLIC/PMDFLTS.INI") == 0,
|
||||
"metadata path builds with trailing volume slash");
|
||||
failures += expect_true(!strcmp(path,
|
||||
"/srv/nw/SYS/.salvage/SUPERVISOR/PUBLIC/PMDFLTS.INI.json"),
|
||||
"metadata path layout");
|
||||
|
||||
failures += expect_true(nwsalvage_build_recycle_path(
|
||||
path, sizeof(path), &config, "/srv/nw/SYS",
|
||||
"../PMDFLTS.INI") < 0,
|
||||
"relative path rejects dot-dot components");
|
||||
failures += expect_true(nwsalvage_build_recycle_path(
|
||||
path, sizeof(path), &config, "/srv/nw/SYS",
|
||||
"/SUPERVISOR/PUBLIC/PMDFLTS.INI") < 0,
|
||||
"relative path rejects absolute input");
|
||||
failures += expect_true(nwsalvage_build_recycle_path(
|
||||
path, sizeof(path), &config, "/srv/nw/SYS",
|
||||
"SUPERVISOR\\PUBLIC\\PMDFLTS.INI") < 0,
|
||||
"relative path rejects unnormalised backslashes");
|
||||
failures += expect_true(nwsalvage_build_metadata_path(
|
||||
path, 16, &config, "/srv/nw/SYS",
|
||||
"SUPERVISOR/PUBLIC/PMDFLTS.INI") < 0,
|
||||
"path builder rejects too-small buffers");
|
||||
|
||||
if (failures)
|
||||
return(1);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user