| |
| /* 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); |