]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/open-file.c
Merge pull request #25168 from valentindavid/valentindavid/umount-move-recursive...
[thirdparty/systemd.git] / src / shared / open-file.c
1
2 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3
4 #include <fcntl.h>
5
6 #include "escape.h"
7 #include "extract-word.h"
8 #include "fd-util.h"
9 #include "open-file.h"
10 #include "path-util.h"
11 #include "string-table.h"
12 #include "string-util.h"
13
14 int open_file_parse(const char *v, OpenFile **ret) {
15 _cleanup_free_ char *options = NULL;
16 _cleanup_(open_file_freep) OpenFile *of = NULL;
17 int r;
18
19 assert(v);
20 assert(ret);
21
22 of = new0(OpenFile, 1);
23 if (!of)
24 return -ENOMEM;
25
26 r = extract_many_words(&v, ":", EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_CUNESCAPE, &of->path, &of->fdname, &options, NULL);
27 if (r < 0)
28 return r;
29 if (r == 0)
30 return -EINVAL;
31
32 /* Enforce that at most 3 colon-separated words are present */
33 if (!isempty(v))
34 return -EINVAL;
35
36 for (const char *p = options;;) {
37 OpenFileFlag flag;
38 _cleanup_free_ char *word = NULL;
39
40 r = extract_first_word(&p, &word, ",", 0);
41 if (r < 0)
42 return r;
43 if (r == 0)
44 break;
45
46 flag = open_file_flags_from_string(word);
47 if (flag < 0)
48 return flag;
49
50 if ((flag & of->flags) != 0)
51 return -EINVAL;
52
53 of->flags |= flag;
54 }
55
56 if (isempty(of->fdname)) {
57 of->fdname = mfree(of->fdname);
58 r = path_extract_filename(of->path, &of->fdname);
59 if (r < 0)
60 return r;
61 }
62
63 r = open_file_validate(of);
64 if (r < 0)
65 return r;
66
67 *ret = TAKE_PTR(of);
68
69 return 0;
70 }
71
72 int open_file_validate(const OpenFile *of) {
73 assert(of);
74
75 if (!path_is_valid(of->path) || !path_is_absolute(of->path))
76 return -EINVAL;
77
78 if (!fdname_is_valid(of->fdname))
79 return -EINVAL;
80
81 if ((FLAGS_SET(of->flags, OPENFILE_READ_ONLY) + FLAGS_SET(of->flags, OPENFILE_APPEND) +
82 FLAGS_SET(of->flags, OPENFILE_TRUNCATE)) > 1)
83 return -EINVAL;
84
85 if ((of->flags & ~_OPENFILE_MASK_PUBLIC) != 0)
86 return -EINVAL;
87
88 return 0;
89 }
90
91 int open_file_to_string(const OpenFile *of, char **ret) {
92 _cleanup_free_ char *options = NULL, *fname = NULL, *s = NULL;
93 bool has_fdname = false;
94 int r;
95
96 assert(of);
97 assert(ret);
98
99 s = shell_escape(of->path, ":");
100 if (!s)
101 return -ENOMEM;
102
103 r = path_extract_filename(of->path, &fname);
104 if (r < 0)
105 return r;
106
107 has_fdname = !streq(fname, of->fdname);
108 if (has_fdname)
109 if (!strextend(&s, ":", of->fdname))
110 return -ENOMEM;
111
112 for (OpenFileFlag flag = OPENFILE_READ_ONLY; flag < _OPENFILE_MAX; flag <<= 1)
113 if (FLAGS_SET(of->flags, flag) && !strextend_with_separator(&options, ",", open_file_flags_to_string(flag)))
114 return -ENOMEM;
115
116 if (options)
117 if (!(has_fdname ? strextend(&s, ":", options) : strextend(&s, "::", options)))
118 return -ENOMEM;
119
120 *ret = TAKE_PTR(s);
121
122 return 0;
123 }
124
125 OpenFile *open_file_free(OpenFile *of) {
126 if (!of)
127 return NULL;
128
129 free(of->path);
130 free(of->fdname);
131 return mfree(of);
132 }
133
134 void open_file_free_many(OpenFile **head) {
135 OpenFile *of;
136
137 while ((of = *head)) {
138 LIST_REMOVE(open_files, *head, of);
139 of = open_file_free(of);
140 }
141 }
142
143 static const char * const open_file_flags_table[_OPENFILE_MAX] = {
144 [OPENFILE_READ_ONLY] = "read-only",
145 [OPENFILE_APPEND] = "append",
146 [OPENFILE_TRUNCATE] = "truncate",
147 [OPENFILE_GRACEFUL] = "graceful",
148 };
149
150 DEFINE_STRING_TABLE_LOOKUP(open_file_flags, OpenFileFlag);