util: rework cunescape(), improve error handling

Change cunescape() to return a normal error code, so that we can
distuingish OOM errors from parse errors.

This also adds a flags parameter to control whether "relaxed" or normal
parsing shall be done. If set no parse failures are generated, and the
only reason why cunescape() can fail is OOM.
This commit is contained in:
Lennart Poettering 2015-04-06 20:11:41 +02:00
parent 64f75d7a28
commit 527b7a421f
14 changed files with 138 additions and 108 deletions

View File

@ -507,16 +507,17 @@ int config_parse_exec_oom_score_adjust(const char* unit,
return 0;
}
int config_parse_exec(const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
int config_parse_exec(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
ExecCommand **e = data, *nce;
char *path, **n;
@ -568,15 +569,13 @@ int config_parse_exec(const char *unit,
word ++;
}
}
} else
if (strneq(word, ";", MAX(l, 1U)))
} else if (strneq(word, ";", MAX(l, 1U)))
goto found;
k++;
}
if (!isempty(state)) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Trailing garbage, ignoring.");
log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Trailing garbage, ignoring.");
return 0;
}
@ -605,9 +604,10 @@ int config_parse_exec(const char *unit,
else
skip = strneq(word, "\\;", MAX(l, 1U));
c = cunescape_length(word + skip, l - skip);
if (!c) {
r = log_oom();
r = cunescape_length(word + skip, l - skip, 0, &c);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to unescape command line, ignoring: %s", rvalue);
r = 0;
goto fail;
}
@ -637,8 +637,7 @@ int config_parse_exec(const char *unit,
else
goto ok;
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"%s, ignoring: %s", reason, rvalue);
log_syntax(unit, LOG_ERR, filename, line, EINVAL, "%s, ignoring: %s", reason, rvalue);
r = 0;
goto fail;
@ -1976,8 +1975,7 @@ int config_parse_environ(const char *unit,
if (u) {
r = unit_full_printf(u, rvalue, &k);
if (r < 0)
log_syntax(unit, LOG_ERR, filename, line, -r,
"Failed to resolve specifiers, ignoring: %s", rvalue);
log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
}
if (!k)
@ -1989,13 +1987,14 @@ int config_parse_environ(const char *unit,
_cleanup_free_ char *n;
char **x;
n = cunescape_length(word, l);
if (!n)
return log_oom();
r = cunescape_length(word, l, 0, &n);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Couldn't unescape assignment, ignoring: %s", rvalue);
continue;
}
if (!env_assignment_is_valid(n)) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Invalid environment assignment, ignoring: %s", rvalue);
log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid environment assignment, ignoring: %s", rvalue);
continue;
}

View File

@ -2426,11 +2426,9 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
_cleanup_free_ char *uce = NULL;
char **e;
uce = cunescape(l+4);
if (!uce) {
r = -ENOMEM;
r = cunescape(l + 4, UNESCAPE_RELAX, &uce);
if (r < 0)
goto finish;
}
e = strv_env_set(m->environment, uce);
if (!e) {

View File

@ -1579,7 +1579,7 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
r = 0;
for (;;) {
const char *device, *path, *options, *fstype;
_cleanup_free_ const char *d = NULL, *p = NULL;
_cleanup_free_ char *d = NULL, *p = NULL;
struct libmnt_fs *fs;
int k;
@ -1594,12 +1594,10 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
options = mnt_fs_get_options(fs);
fstype = mnt_fs_get_fstype(fs);
d = cunescape(device);
if (!d)
if (cunescape(device, UNESCAPE_RELAX, &d) < 0)
return log_oom();
p = cunescape(path);
if (!p)
if (cunescape(path, UNESCAPE_RELAX, &p) < 0)
return log_oom();
(void) device_found_node(m, d, true, DEVICE_FOUND_MOUNT, set_flags);

View File

@ -1099,8 +1099,7 @@ static int swap_load_proc_swaps(Manager *m, bool set_flags) {
continue;
}
d = cunescape(dev);
if (!d)
if (cunescape(dev, UNESCAPE_RELAX, &d) < 0)
return log_oom();
device_found_node(m, d, true, DEVICE_FOUND_SWAP, set_flags);

View File

@ -62,6 +62,7 @@ static void mount_points_list_free(MountPoint **head) {
static int mount_points_list_get(MountPoint **head) {
_cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
unsigned int i;
int r;
assert(head);
@ -97,9 +98,9 @@ static int mount_points_list_get(MountPoint **head) {
continue;
}
p = cunescape(path);
if (!p)
return -ENOMEM;
r = cunescape(path, UNESCAPE_RELAX, &p);
if (r < 0)
return r;
/* Ignore mount points we can't unmount because they
* are API or because we are keeping them open (like
@ -133,10 +134,12 @@ static int mount_points_list_get(MountPoint **head) {
static int swap_list_get(MountPoint **head) {
_cleanup_fclose_ FILE *proc_swaps = NULL;
unsigned int i;
int r;
assert(head);
if (!(proc_swaps = fopen("/proc/swaps", "re")))
proc_swaps = fopen("/proc/swaps", "re");
if (!proc_swaps)
return (errno == ENOENT) ? 0 : -errno;
(void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n");
@ -146,19 +149,19 @@ static int swap_list_get(MountPoint **head) {
char *dev = NULL, *d;
int k;
if ((k = fscanf(proc_swaps,
"%ms " /* device/file */
"%*s " /* type of swap */
"%*s " /* swap size */
"%*s " /* used */
"%*s\n", /* priority */
&dev)) != 1) {
k = fscanf(proc_swaps,
"%ms " /* device/file */
"%*s " /* type of swap */
"%*s " /* swap size */
"%*s " /* used */
"%*s\n", /* priority */
&dev);
if (k != 1) {
if (k == EOF)
break;
log_warning("Failed to parse /proc/swaps:%u.", i);
free(dev);
continue;
}
@ -168,14 +171,13 @@ static int swap_list_get(MountPoint **head) {
continue;
}
d = cunescape(dev);
r = cunescape(dev, UNESCAPE_RELAX, &d);
free(dev);
if (r < 0)
return r;
if (!d) {
return -ENOMEM;
}
if (!(swap = new0(MountPoint, 1))) {
swap = new0(MountPoint, 1);
if (!swap) {
free(d);
return -ENOMEM;
}

View File

@ -92,9 +92,9 @@ int pull_find_old_etags(const char *url, const char *image_root, int dt, const c
if (a >= b)
continue;
u = cunescape_length(a, b - a);
if (!u)
return -ENOMEM;
r = cunescape_length(a, b - a, 0, &u);
if (r < 0)
return r;
if (!http_etag_is_valid(u)) {
free(u);

View File

@ -201,8 +201,7 @@ static void dev_kmsg_record(Server *s, const char *p, size_t l) {
*e = 0;
m = cunescape_length_with_prefix(k, e - k, "_KERNEL_");
if (!m)
if (cunescape_length_with_prefix(k, e - k, "_KERNEL_", UNESCAPE_RELAX, &m) < 0)
break;
if (startswith(m, "_KERNEL_DEVICE="))
@ -299,8 +298,7 @@ static void dev_kmsg_record(Server *s, const char *p, size_t l) {
}
}
message = cunescape_length_with_prefix(p, pl, "MESSAGE=");
if (message)
if (cunescape_length_with_prefix(p, pl, "MESSAGE=", UNESCAPE_RELAX, &message) >= 0)
IOVEC_SET_STRING(iovec[n++], message);
server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), NULL, NULL, NULL, 0, NULL, priority, 0);

View File

@ -496,9 +496,9 @@ _public_ int sd_session_get_desktop(const char *session, char **desktop) {
if (r < 0)
return r;
t = cunescape(escaped);
if (!t)
return -ENOMEM;
r = cunescape(escaped, 0, &t);
if (r < 0)
return r;
*desktop = t;
return 0;

View File

@ -253,8 +253,7 @@ int devnode_acl_all(struct udev *udev,
FOREACH_DIRENT(dent, dir, return -errno) {
_cleanup_free_ char *unescaped_devname = NULL;
unescaped_devname = cunescape(dent->d_name);
if (!unescaped_devname)
if (cunescape(dent->d_name, UNESCAPE_RELAX, &unescaped_devname) < 0)
return -ENOMEM;
n = strappend("/dev/", unescaped_devname);

View File

@ -231,18 +231,18 @@ int inhibitor_load(Inhibitor *i) {
}
if (who) {
cc = cunescape(who);
if (!cc)
return -ENOMEM;
r = cunescape(who, 0, &cc);
if (r < 0)
return r;
free(i->who);
i->who = cc;
}
if (why) {
cc = cunescape(why);
if (!cc)
return -ENOMEM;
r = cunescape(why, 0, &cc);
if (r < 0)
return r;
free(i->why);
i->why = cc;

View File

@ -1466,12 +1466,13 @@ static int cunescape_one(const char *p, size_t length, char *ret) {
return r;
}
char *cunescape_length_with_prefix(const char *s, size_t length, const char *prefix) {
int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) {
char *r, *t;
const char *f;
size_t pl;
assert(s);
assert(ret);
/* Undoes C style string escaping, and optionally prefixes it. */
@ -1479,7 +1480,7 @@ char *cunescape_length_with_prefix(const char *s, size_t length, const char *pre
r = new(char, pl+length+1);
if (!r)
return NULL;
return -ENOMEM;
if (prefix)
memcpy(r, prefix, pl);
@ -1491,17 +1492,31 @@ char *cunescape_length_with_prefix(const char *s, size_t length, const char *pre
remaining = s + length - f;
assert(remaining > 0);
if (*f != '\\' || remaining == 1) {
/* a literal literal, or a trailing backslash, copy verbatim */
if (*f != '\\') {
/* A literal literal, copy verbatim */
*(t++) = *f;
continue;
}
if (remaining == 1) {
if (flags & UNESCAPE_RELAX) {
/* A trailing backslash, copy verbatim */
*(t++) = *f;
continue;
}
return -EINVAL;
}
k = cunescape_one(f + 1, remaining - 1, t);
if (k < 0) {
/* Invalid escape code, let's take it literal then */
*(t++) = '\\';
continue;
if (flags & UNESCAPE_RELAX) {
/* Invalid escape code, let's take it literal then */
*(t++) = '\\';
continue;
}
return k;
}
f += k;
@ -1509,17 +1524,17 @@ char *cunescape_length_with_prefix(const char *s, size_t length, const char *pre
}
*t = 0;
return r;
*ret = r;
return t - r;
}
char *cunescape_length(const char *s, size_t length) {
return cunescape_length_with_prefix(s, length, NULL);
int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
return cunescape_length_with_prefix(s, length, NULL, flags, ret);
}
char *cunescape(const char *s) {
assert(s);
return cunescape_length(s, strlen(s));
int cunescape(const char *s, UnescapeFlags flags, char **ret) {
return cunescape_length(s, strlen(s), flags, ret);
}
char *xescape(const char *s, const char *bad) {
@ -6731,9 +6746,9 @@ int umount_recursive(const char *prefix, int flags) {
continue;
}
p = cunescape(path);
if (!p)
return -ENOMEM;
r = cunescape(path, UNESCAPE_RELAX, &p);
if (r < 0)
return r;
if (!path_startswith(p, prefix))
continue;
@ -6833,9 +6848,9 @@ int bind_remount_recursive(const char *prefix, bool ro) {
continue;
}
p = cunescape(path);
if (!p)
return -ENOMEM;
r = cunescape(path, UNESCAPE_RELAX, &p);
if (r < 0)
return r;
/* Let's ignore autofs mounts. If they aren't
* triggered yet, we want to avoid triggering

View File

@ -311,9 +311,14 @@ char decchar(int x) _const_;
int undecchar(char c) _const_;
char *cescape(const char *s);
char *cunescape(const char *s);
char *cunescape_length(const char *s, size_t length);
char *cunescape_length_with_prefix(const char *s, size_t length, const char *prefix);
typedef enum UnescapeFlags {
UNESCAPE_RELAX = 1,
} UnescapeFlags;
int cunescape(const char *s, UnescapeFlags flags, char **ret);
int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret);
int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret);
char *xescape(const char *s, const char *bad);
@ -1014,7 +1019,7 @@ int take_password_lock(const char *root);
int is_symlink(const char *path);
int is_dir(const char *path, bool follow);
typedef enum UnquoteFlags{
typedef enum UnquoteFlags {
UNQUOTE_RELAX = 1,
UNQUOTE_CUNESCAPE = 2,
} UnquoteFlags;

View File

@ -417,23 +417,40 @@ static void test_cescape(void) {
static void test_cunescape(void) {
_cleanup_free_ char *unescaped;
unescaped = cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00");
assert_se(streq_ptr(unescaped, "abc\\\"\b\f\a\n\r\t\v\003\177\234\313\\000\\x00"));
assert_se(cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00", 0, &unescaped) < 0);
assert_se(cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00", UNESCAPE_RELAX, &unescaped) >= 0);
const char *x = "abc\\\"\b\f\a\n\r\t\v\003\177\234\313\\000\\x00";
assert_se(streq_ptr(unescaped, x));
free(unescaped);
unescaped = NULL;
/* incomplete sequences */
unescaped = cunescape("\\x0");
assert_se(cunescape("\\x0", 0, &unescaped) < 0);
assert_se(cunescape("\\x0", UNESCAPE_RELAX, &unescaped) >= 0);
assert_se(streq_ptr(unescaped, "\\x0"));
free(unescaped);
unescaped = NULL;
unescaped = cunescape("\\x");
assert_se(cunescape("\\x", 0, &unescaped) < 0);
assert_se(cunescape("\\x", UNESCAPE_RELAX, &unescaped) >= 0);
assert_se(streq_ptr(unescaped, "\\x"));
free(unescaped);
unescaped = NULL;
unescaped = cunescape("\\");
assert_se(cunescape("\\", 0, &unescaped) < 0);
assert_se(cunescape("\\", UNESCAPE_RELAX, &unescaped) >= 0);
assert_se(streq_ptr(unescaped, "\\"));
free(unescaped);
unescaped = NULL;
unescaped = cunescape("\\11");
assert_se(cunescape("\\11", 0, &unescaped) < 0);
assert_se(cunescape("\\11", UNESCAPE_RELAX, &unescaped) >= 0);
assert_se(streq_ptr(unescaped, "\\11"));
free(unescaped);
unescaped = NULL;
unescaped = cunescape("\\1");
assert_se(cunescape("\\1", 0, &unescaped) < 0);
assert_se(cunescape("\\1", UNESCAPE_RELAX, &unescaped) >= 0);
assert_se(streq_ptr(unescaped, "\\1"));
}

View File

@ -913,13 +913,13 @@ static int write_one_file(Item *i, const char *path) {
}
if (i->argument) {
_cleanup_free_ char *unescaped;
_cleanup_free_ char *unescaped = NULL;
log_debug("%s to \"%s\".", i->type == CREATE_FILE ? "Appending" : "Writing", path);
unescaped = cunescape(i->argument);
if (!unescaped)
return log_oom();
r = cunescape(i->argument, 0, &unescaped);
if (r < 0)
return log_error_errno(r, "Failed to unescape parameter to write: %s", i->argument);
r = loop_write(fd, unescaped, strlen(unescaped), false);
if (r < 0)