blob: 3402518555b3589a36c676086a2512cad3ed09bd [file] [log] [blame]
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <fcntl.h>
#include "escape.h"
#include "extract-word.h"
#include "fd-util.h"
#include "open-file.h"
#include "path-util.h"
#include "string-table.h"
#include "string-util.h"
int open_file_parse(const char *v, OpenFile **ret) {
_cleanup_free_ char *options = NULL;
_cleanup_(open_file_freep) OpenFile *of = NULL;
int r;
assert(v);
assert(ret);
of = new0(OpenFile, 1);
if (!of)
return -ENOMEM;
r = extract_many_words(&v, ":", EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_CUNESCAPE, &of->path, &of->fdname, &options, NULL);
if (r < 0)
return r;
if (r == 0)
return -EINVAL;
/* Enforce that at most 3 colon-separated words are present */
if (!isempty(v))
return -EINVAL;
for (const char *p = options;;) {
OpenFileFlag flag;
_cleanup_free_ char *word = NULL;
r = extract_first_word(&p, &word, ",", 0);
if (r < 0)
return r;
if (r == 0)
break;
flag = open_file_flags_from_string(word);
if (flag < 0)
return flag;
if ((flag & of->flags) != 0)
return -EINVAL;
of->flags |= flag;
}
if (isempty(of->fdname)) {
of->fdname = mfree(of->fdname);
r = path_extract_filename(of->path, &of->fdname);
if (r < 0)
return r;
}
r = open_file_validate(of);
if (r < 0)
return r;
*ret = TAKE_PTR(of);
return 0;
}
int open_file_validate(const OpenFile *of) {
assert(of);
if (!path_is_valid(of->path) || !path_is_absolute(of->path))
return -EINVAL;
if (!fdname_is_valid(of->fdname))
return -EINVAL;
if ((FLAGS_SET(of->flags, OPENFILE_READ_ONLY) + FLAGS_SET(of->flags, OPENFILE_APPEND) +
FLAGS_SET(of->flags, OPENFILE_TRUNCATE)) > 1)
return -EINVAL;
if ((of->flags & ~_OPENFILE_MASK_PUBLIC) != 0)
return -EINVAL;
return 0;
}
int open_file_to_string(const OpenFile *of, char **ret) {
_cleanup_free_ char *options = NULL, *fname = NULL, *s = NULL;
bool has_fdname = false;
int r;
assert(of);
assert(ret);
s = shell_escape(of->path, ":");
if (!s)
return -ENOMEM;
r = path_extract_filename(of->path, &fname);
if (r < 0)
return r;
has_fdname = !streq(fname, of->fdname);
if (has_fdname)
if (!strextend(&s, ":", of->fdname))
return -ENOMEM;
for (OpenFileFlag flag = OPENFILE_READ_ONLY; flag < _OPENFILE_MAX; flag <<= 1)
if (FLAGS_SET(of->flags, flag) && !strextend_with_separator(&options, ",", open_file_flags_to_string(flag)))
return -ENOMEM;
if (options)
if (!(has_fdname ? strextend(&s, ":", options) : strextend(&s, "::", options)))
return -ENOMEM;
*ret = TAKE_PTR(s);
return 0;
}
OpenFile *open_file_free(OpenFile *of) {
if (!of)
return NULL;
free(of->path);
free(of->fdname);
return mfree(of);
}
void open_file_free_many(OpenFile **head) {
OpenFile *of;
while ((of = *head)) {
LIST_REMOVE(open_files, *head, of);
of = open_file_free(of);
}
}
static const char * const open_file_flags_table[_OPENFILE_MAX] = {
[OPENFILE_READ_ONLY] = "read-only",
[OPENFILE_APPEND] = "append",
[OPENFILE_TRUNCATE] = "truncate",
[OPENFILE_GRACEFUL] = "graceful",
};
DEFINE_STRING_TABLE_LOOKUP(open_file_flags, OpenFileFlag);