/src/barebox/common/file-list.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-only |
2 | | |
3 | | #define pr_fmt(fmt) "file_list: " fmt |
4 | | |
5 | | #include <common.h> |
6 | | #include <malloc.h> |
7 | | #include <fs.h> |
8 | | #include <file-list.h> |
9 | | #include <stringlist.h> |
10 | | #include <linux/err.h> |
11 | | #include <driver.h> |
12 | | #include <block.h> |
13 | | |
14 | 0 | #define PARSE_DEVICE 0 |
15 | 0 | #define PARSE_NAME 1 |
16 | 0 | #define PARSE_FLAGS 2 |
17 | | |
18 | | struct file_list_entry *file_list_entry_by_name(struct file_list *files, const char *name) |
19 | 0 | { |
20 | 0 | struct file_list_entry *entry; |
21 | |
|
22 | 0 | file_list_for_each_entry(files, entry) { |
23 | 0 | if (!strcmp(entry->name, name)) |
24 | 0 | return entry; |
25 | 0 | } |
26 | | |
27 | 0 | return NULL; |
28 | 0 | } |
29 | | |
30 | | static int __file_list_add_entry(struct file_list *files, |
31 | | const char *name, |
32 | | const char *filename, |
33 | | unsigned long flags) |
34 | 0 | { |
35 | 0 | struct file_list_entry *entry; |
36 | |
|
37 | 0 | entry = file_list_entry_by_name(files, name); |
38 | 0 | if (entry) |
39 | 0 | return -EEXIST; |
40 | | |
41 | 0 | entry = xzalloc(sizeof(*entry)); |
42 | |
|
43 | 0 | entry->name = name; |
44 | 0 | entry->filename = filename; |
45 | 0 | entry->flags = flags; |
46 | |
|
47 | 0 | list_add_tail(&entry->list, &files->list); |
48 | |
|
49 | 0 | return 0; |
50 | 0 | } |
51 | | |
52 | | int file_list_add_entry(struct file_list *files, const char *name, const char *filename, |
53 | | unsigned long flags) |
54 | 0 | { |
55 | 0 | return __file_list_add_entry(files, |
56 | 0 | xstrdup_const(name), xstrdup_const(filename), |
57 | 0 | flags); |
58 | 0 | } |
59 | | |
60 | | int file_list_add_cdev_entry(struct file_list *files, struct cdev *cdev, |
61 | | unsigned long flags) |
62 | 0 | { |
63 | 0 | return __file_list_add_entry(files, xstrdup_const(cdev->name), |
64 | 0 | xasprintf("/dev/%s", cdev->name), flags); |
65 | 0 | } |
66 | | |
67 | | static bool file_list_handle_spec(struct file_list *files, const char *spec) |
68 | 0 | { |
69 | 0 | unsigned count = 0; |
70 | 0 | bool autoadd; |
71 | |
|
72 | 0 | autoadd = !strcmp(spec, "auto"); |
73 | 0 | if (autoadd || !strcmp(spec, "block")) |
74 | 0 | count += file_list_add_blockdevs(files); |
75 | 0 | else |
76 | 0 | return false; |
77 | | |
78 | 0 | pr_debug("'%s' spcifier resulted in %u entries\n", spec, count); |
79 | 0 | return true; |
80 | 0 | } |
81 | | |
82 | | static int file_list_parse_one(struct file_list *files, const char *partstr, const char **endstr) |
83 | 0 | { |
84 | 0 | int i = 0, state = PARSE_DEVICE; |
85 | 0 | char filename[PATH_MAX]; |
86 | 0 | char name[PATH_MAX]; |
87 | 0 | unsigned long flags = 0; |
88 | 0 | bool special = false; |
89 | |
|
90 | 0 | memset(filename, 0, sizeof(filename)); |
91 | 0 | memset(name, 0, sizeof(name)); |
92 | |
|
93 | 0 | while (*partstr && *partstr != ',') { |
94 | 0 | switch (state) { |
95 | 0 | case PARSE_DEVICE: |
96 | 0 | if (*partstr == '(') { |
97 | 0 | state = PARSE_NAME; |
98 | 0 | i = 0; |
99 | 0 | } else { |
100 | 0 | filename[i++] = *partstr; |
101 | 0 | } |
102 | 0 | break; |
103 | 0 | case PARSE_NAME: |
104 | 0 | if (*partstr == ')') { |
105 | 0 | state = PARSE_FLAGS; |
106 | 0 | i = 0; |
107 | 0 | } else { |
108 | 0 | name[i++] = *partstr; |
109 | 0 | } |
110 | 0 | break; |
111 | 0 | case PARSE_FLAGS: |
112 | 0 | switch (*partstr) { |
113 | 0 | case 's': |
114 | 0 | flags |= FILE_LIST_FLAG_SAFE; |
115 | 0 | break; |
116 | 0 | case 'r': |
117 | 0 | flags |= FILE_LIST_FLAG_READBACK; |
118 | 0 | break; |
119 | 0 | case 'c': |
120 | 0 | flags |= FILE_LIST_FLAG_CREATE; |
121 | 0 | break; |
122 | 0 | case 'u': |
123 | 0 | flags |= FILE_LIST_FLAG_UBI; |
124 | 0 | break; |
125 | 0 | case 'o': |
126 | 0 | flags |= FILE_LIST_FLAG_OPTIONAL; |
127 | 0 | break; |
128 | 0 | default: |
129 | 0 | pr_err("Unknown flag '%c'\n", *partstr); |
130 | 0 | return -EINVAL; |
131 | 0 | } |
132 | 0 | break; |
133 | 0 | default: |
134 | 0 | return -EINVAL; |
135 | 0 | } |
136 | 0 | partstr++; |
137 | 0 | } |
138 | | |
139 | 0 | if (state == PARSE_DEVICE) |
140 | 0 | special = file_list_handle_spec(files, filename); |
141 | |
|
142 | 0 | if (!special && state != PARSE_FLAGS) { |
143 | 0 | pr_err("Missing ')'\n"); |
144 | 0 | return -EINVAL; |
145 | 0 | } |
146 | | |
147 | 0 | if (*partstr == ',') |
148 | 0 | partstr++; |
149 | 0 | *endstr = partstr; |
150 | |
|
151 | 0 | return special ? 0 : file_list_add_entry(files, name, filename, flags); |
152 | 0 | } |
153 | | |
154 | | static const char *flags_to_str(int flags) |
155 | 0 | { |
156 | 0 | static char str[sizeof "srcuo"]; |
157 | 0 | char *s = str;; |
158 | |
|
159 | 0 | if (flags & FILE_LIST_FLAG_SAFE) |
160 | 0 | *s++ = 's'; |
161 | 0 | if (flags & FILE_LIST_FLAG_READBACK) |
162 | 0 | *s++ = 'r'; |
163 | 0 | if (flags & FILE_LIST_FLAG_CREATE) |
164 | 0 | *s++ = 'c'; |
165 | 0 | if (flags & FILE_LIST_FLAG_UBI) |
166 | 0 | *s++ = 'u'; |
167 | 0 | if (flags & FILE_LIST_FLAG_OPTIONAL) |
168 | 0 | *s++ = 'o'; |
169 | |
|
170 | 0 | *s = '\0'; |
171 | |
|
172 | 0 | return str; |
173 | 0 | } |
174 | | |
175 | | struct file_list *file_list_new(void) |
176 | 0 | { |
177 | 0 | struct file_list *files; |
178 | |
|
179 | 0 | files = xzalloc(sizeof(*files)); |
180 | |
|
181 | 0 | INIT_LIST_HEAD(&files->list); |
182 | |
|
183 | 0 | return files; |
184 | 0 | } |
185 | | |
186 | | struct file_list *file_list_parse(const char *str) |
187 | 0 | { |
188 | 0 | struct file_list *files; |
189 | 0 | int ret; |
190 | 0 | const char *endptr; |
191 | |
|
192 | 0 | files = file_list_new(); |
193 | |
|
194 | 0 | while (*str) { |
195 | 0 | ret = file_list_parse_one(files, str, &endptr); |
196 | 0 | if (ret) { |
197 | 0 | pr_err("parse error\n"); |
198 | 0 | goto out; |
199 | 0 | } |
200 | 0 | str = endptr; |
201 | 0 | } |
202 | | |
203 | 0 | return files; |
204 | 0 | out: |
205 | 0 | file_list_free(files); |
206 | |
|
207 | 0 | return ERR_PTR(ret); |
208 | 0 | } |
209 | | |
210 | | struct file_list *file_list_parse_null(const char *files) |
211 | 0 | { |
212 | 0 | struct file_list *list; |
213 | |
|
214 | 0 | if (!files) |
215 | 0 | return NULL; |
216 | | |
217 | 0 | list = file_list_parse(files); |
218 | 0 | if (IS_ERR(list)) { |
219 | 0 | pr_err("Parsing file list \"%s\" failed: %pe\n", files, list); |
220 | 0 | return NULL; |
221 | 0 | } |
222 | | |
223 | 0 | return list; |
224 | 0 | } |
225 | | |
226 | | void file_list_free(struct file_list *files) |
227 | 0 | { |
228 | 0 | struct file_list_entry *entry, *tmp; |
229 | |
|
230 | 0 | if (!files) |
231 | 0 | return; |
232 | | |
233 | 0 | list_for_each_entry_safe(entry, tmp, &files->list, list) { |
234 | 0 | free_const(entry->name); |
235 | 0 | free_const(entry->filename); |
236 | 0 | free(entry); |
237 | 0 | } |
238 | |
|
239 | 0 | free(files); |
240 | 0 | } |
241 | | |
242 | | struct file_list *file_list_dup(struct file_list *old) |
243 | 0 | { |
244 | 0 | struct file_list_entry *old_entry; |
245 | 0 | struct file_list *new; |
246 | |
|
247 | 0 | new = file_list_new(); |
248 | |
|
249 | 0 | list_for_each_entry(old_entry, &old->list, list) |
250 | 0 | (void)file_list_add_entry(new, old_entry->name, old_entry->filename, |
251 | 0 | old_entry->flags); /* can't fail */ |
252 | |
|
253 | 0 | return new; |
254 | 0 | } |
255 | | |
256 | | char *file_list_to_str(const struct file_list *files) |
257 | 0 | { |
258 | 0 | struct file_list_entry *entry; |
259 | 0 | struct string_list sl; |
260 | 0 | char *str; |
261 | |
|
262 | 0 | if (!files) |
263 | 0 | return strdup(""); |
264 | | |
265 | 0 | string_list_init(&sl); |
266 | |
|
267 | 0 | list_for_each_entry(entry, &files->list, list) { |
268 | 0 | int ret = string_list_add_asprintf(&sl, "%s(%s)%s", entry->filename, entry->name, |
269 | 0 | flags_to_str(entry->flags)); |
270 | 0 | if (ret) { |
271 | 0 | str = ERR_PTR(ret); |
272 | 0 | goto out; |
273 | 0 | } |
274 | 0 | } |
275 | | |
276 | 0 | str = string_list_join(&sl, ","); |
277 | 0 | out: |
278 | 0 | string_list_free(&sl); |
279 | |
|
280 | 0 | return str; |
281 | 0 | } |
282 | | |
283 | | int file_list_detect_all(const struct file_list *files) |
284 | 0 | { |
285 | 0 | struct file_list_entry *fentry; |
286 | 0 | struct stat s; |
287 | 0 | int i = 0; |
288 | |
|
289 | 0 | list_for_each_entry(fentry, &files->list, list) { |
290 | 0 | if (!stat(fentry->filename, &s)) |
291 | 0 | continue; |
292 | 0 | if (device_detect_by_name(devpath_to_name(fentry->filename)) == 0) |
293 | 0 | i++; |
294 | 0 | } |
295 | |
|
296 | 0 | return i; |
297 | 0 | } |