1 /*
2 * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
3 * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for
6 * any purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all
8 * copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 #include "qdf_file.h"
21 #include "qdf_module.h"
22 #include "qdf_parse.h"
23 #include "qdf_status.h"
24 #include "qdf_str.h"
25 #include "qdf_trace.h"
26 #include "qdf_types.h"
27
28 #ifdef WLAN_USE_CONFIG_PARAMS
29 #define QDF_SECTION_FOUND break
30 #else
31 #define QDF_SECTION_FOUND continue
32 #endif
33
qdf_ini_read_values(char ** main_cursor,char ** read_key,char ** read_value,bool * section_item)34 static QDF_STATUS qdf_ini_read_values(char **main_cursor,
35 char **read_key, char **read_value,
36 bool *section_item)
37 {
38 char *cursor = *main_cursor;
39
40 /* foreach line */
41 while (*cursor != '\0') {
42 char *key = cursor;
43 char *value = NULL;
44 bool comment = false;
45 bool eol = false;
46
47 /*
48 * Look for the end of the line, while noting any
49 * value ('=') or comment ('#') indicators
50 */
51 while (!eol) {
52 switch (*cursor) {
53 case '\r':
54 case '\n':
55 *cursor = '\0';
56 cursor++;
57 fallthrough;
58 case '\0':
59 eol = true;
60 break;
61
62 case '=':
63 /*
64 * The first '=' is the value indicator.
65 * Subsequent '=' are valid value characters.
66 */
67 if (!value && !comment) {
68 value = cursor + 1;
69 *cursor = '\0';
70 }
71
72 cursor++;
73 break;
74
75 case '#':
76 /*
77 * We don't process comments, so we can null-
78 * terminate unconditionally here (unlike '=').
79 */
80 comment = true;
81 *cursor = '\0';
82 fallthrough;
83 default:
84 cursor++;
85 break;
86 }
87 }
88
89 key = qdf_str_trim(key);
90 /*
91 * Ignoring comments, a valid ini line contains one of:
92 * 1) some 'key=value' config item
93 * 2) section header
94 * 3) a line containing whitespace
95 */
96 if (value) {
97 *read_key = key;
98 *read_value = value;
99 *section_item = 0;
100 *main_cursor = cursor;
101 return QDF_STATUS_SUCCESS;
102 } else if (key[0] == '[') {
103 qdf_size_t len = qdf_str_len(key);
104
105 if (key[len - 1] != ']') {
106 qdf_err("Invalid *.ini syntax '%s'", key);
107 return QDF_STATUS_E_INVAL;
108 } else {
109 key[len - 1] = '\0';
110 *read_key = key + 1;
111 *section_item = 1;
112 *main_cursor = cursor;
113 return QDF_STATUS_SUCCESS;
114 }
115 } else if (key[0] != '\0') {
116 if (!__qdf_str_cmp(key, "END")) {
117 key[3] = '\0';
118 *section_item = 0;
119 *main_cursor = cursor;
120 *read_key = key;
121 return QDF_STATUS_SUCCESS;
122 }
123 qdf_err("Invalid *.ini syntax '%s'", key);
124 return QDF_STATUS_E_INVAL;
125 }
126
127 /* skip remaining EoL characters */
128 while (*cursor == '\n' || *cursor == '\r')
129 cursor++;
130 }
131
132 return QDF_STATUS_E_INVAL;
133 }
134
qdf_ini_parse(const char * ini_path,void * context,qdf_ini_item_cb item_cb,qdf_ini_section_cb section_cb)135 QDF_STATUS qdf_ini_parse(const char *ini_path, void *context,
136 qdf_ini_item_cb item_cb, qdf_ini_section_cb section_cb)
137 {
138 QDF_STATUS status;
139 char *read_key;
140 char *read_value;
141 bool section_item;
142 int ini_read_count = 0;
143 char *fbuf;
144 char *cursor;
145
146 if (qdf_str_eq(QDF_WIFI_MODULE_PARAMS_FILE, ini_path))
147 status = qdf_module_param_file_read(ini_path, &fbuf);
148 else
149 status = qdf_file_read(ini_path, &fbuf);
150 if (QDF_IS_STATUS_ERROR(status)) {
151 qdf_err("Failed to read *.ini file @ %s", ini_path);
152 return status;
153 }
154
155 /* foreach line */
156 cursor = fbuf;
157
158 while (qdf_ini_read_values(&cursor, &read_key, &read_value,
159 §ion_item) == QDF_STATUS_SUCCESS) {
160 if (!__qdf_str_cmp(read_key, "END"))
161 break;
162
163 if (!section_item) {
164 status = item_cb(context, read_key, read_value);
165 if (QDF_IS_STATUS_ERROR(status))
166 break;
167 else
168 ini_read_count++;
169 } else {
170 qdf_debug("Section started in global file");
171 /* Currently AP Platforms supports and uses Sections,
172 * hence break the loop, sections will be parsed separately,
173 * in case of non AP platforms, sections are used as
174 * logical separators hence continue reading the values.
175 */
176 QDF_SECTION_FOUND;
177 }
178 }
179
180 qdf_info("INI values read: %d", ini_read_count);
181 if (ini_read_count != 0) {
182 qdf_info("INI file parse successful");
183 status = QDF_STATUS_SUCCESS;
184 } else {
185 qdf_info("INI file parse fail: invalid file format");
186 status = QDF_STATUS_E_INVAL;
187 }
188
189 if (qdf_str_eq(QDF_WIFI_MODULE_PARAMS_FILE, ini_path))
190 qdf_module_param_file_free(fbuf);
191 else
192 qdf_file_buf_free(fbuf);
193
194 return status;
195 }
196
197 qdf_export_symbol(qdf_ini_parse);
198
qdf_ini_section_parse(const char * ini_path,void * context,qdf_ini_item_cb item_cb,const char * section_name)199 QDF_STATUS qdf_ini_section_parse(const char *ini_path, void *context,
200 qdf_ini_item_cb item_cb,
201 const char *section_name)
202 {
203 QDF_STATUS status;
204 char *read_key;
205 char *read_value;
206 bool section_item;
207 bool section_found = 0;
208 bool section_complete = 0;
209 int ini_read_count = 0;
210 char *fbuf;
211 char *cursor;
212
213 if (qdf_str_eq(QDF_WIFI_MODULE_PARAMS_FILE, ini_path))
214 status = qdf_module_param_file_read(ini_path, &fbuf);
215 else
216 status = qdf_file_read(ini_path, &fbuf);
217 if (QDF_IS_STATUS_ERROR(status)) {
218 qdf_err("Failed to read *.ini file @ %s", ini_path);
219 return status;
220 }
221
222 /* foreach line */
223 cursor = fbuf;
224
225 while (qdf_ini_read_values(&cursor, &read_key, &read_value,
226 §ion_item) == QDF_STATUS_SUCCESS) {
227 if (!__qdf_str_cmp(read_key, "END"))
228 break;
229
230 if (section_item) {
231 if (qdf_str_cmp(read_key, section_name) == 0) {
232 section_found = 1;
233 section_complete = 0;
234 } else {
235 if (section_found == 1)
236 section_complete = 1;
237 section_found = 0;
238 }
239 } else if (section_found) {
240 status = item_cb(context, read_key, read_value);
241 if (QDF_IS_STATUS_ERROR(status))
242 break;
243 else
244 ini_read_count++;
245 } else if (section_complete) {
246 break;
247 }
248 }
249
250 qdf_info("INI values parse successful read: %d from section %s",
251 ini_read_count, section_name);
252
253 if (ini_read_count != 0) {
254 status = QDF_STATUS_SUCCESS;
255 } else {
256 qdf_debug("INI file parse fail: Section not found %s",
257 section_name);
258 status = QDF_STATUS_SUCCESS;
259 }
260
261 if (qdf_str_eq(QDF_WIFI_MODULE_PARAMS_FILE, ini_path))
262 qdf_module_param_file_free(fbuf);
263 else
264 qdf_file_buf_free(fbuf);
265
266 return status;
267 }
268
269 qdf_export_symbol(qdf_ini_section_parse);
270
271 /**
272 * qdf_validate_key() - Check if ini key is valid
273 * @key: Pointer to key
274 *
275 * Return: Return SUCCESS if key is valid else return INVALID
276 */
qdf_validate_key(char * key)277 static QDF_STATUS qdf_validate_key(char *key)
278 {
279 int i;
280
281 for (i = 0; key[i] != '\0' ; i++) {
282 if ((key[i] >= 'a' && key[i] <= 'z') ||
283 (key[i] >= 'A' && key[i] <= 'Z') ||
284 (key[i] >= '0' && key[i] <= '9') ||
285 (key[i] == '_'))
286 continue;
287 else
288 return QDF_STATUS_E_INVAL;
289 }
290 return QDF_STATUS_SUCCESS;
291 }
292
293 /**
294 * qdf_validate_value() - Validate ini value
295 * @value: Pointer to ini value
296 *
297 * Return: Return SUCCESS if value is valid else return INVALID
298 */
qdf_validate_value(char * value)299 static QDF_STATUS qdf_validate_value(char *value)
300 {
301 int i;
302
303 for (i = 0; value[i] != '\0'; i++) {
304 if ((value[i] >= '0' && value[i] <= '9') ||
305 (value[i] >= 'A' && value[i] <= 'Z') ||
306 (value[i] >= 'a' && value[i] <= 'z') ||
307 value[i] == ',' || value[i] == ':' ||
308 value[i] == '-' || value[i] == '+')
309 continue;
310 else
311 return QDF_STATUS_E_INVAL;
312 }
313 return QDF_STATUS_SUCCESS;
314 }
315
316 /**
317 * qdf_check_ini_validity() - Check if inis are valid
318 * @main_cursor: Pointer to main cursor
319 *
320 * Return: Return true if all inis are valid else false
321 */
qdf_check_ini_validity(char ** main_cursor)322 static bool qdf_check_ini_validity(char **main_cursor)
323 {
324 char *cursor = *main_cursor;
325 char *read_key;
326 char *read_value;
327 bool section_item;
328 QDF_STATUS status = QDF_STATUS_SUCCESS;
329
330 while (*cursor != '\0') {
331 unsigned char *val = (unsigned char *)cursor;
332
333 switch (*cursor) {
334 case '\r':
335 case '\n':
336 cursor++;
337 break;
338 case '\0':
339 break;
340 case '=':
341 case '#':
342 case ']':
343 case '[':
344 case '_':
345 case '-':
346 case ' ':
347 case ':':
348 case ',':
349 cursor++;
350 break;
351
352 default:
353 if (!isalnum(*val)) {
354 qdf_err("Found invalid character %c", *cursor);
355 return false;
356 }
357 cursor++;
358 break;
359 }
360 }
361
362 cursor = *main_cursor;
363 while ((status = qdf_ini_read_values(main_cursor, &read_key,
364 &read_value, §ion_item)) == QDF_STATUS_SUCCESS) {
365 if (!section_item) {
366 if (!__qdf_str_cmp(read_key, "END"))
367 return true;
368 status = qdf_validate_key(read_key);
369 if (QDF_IS_STATUS_ERROR(status)) {
370 status = QDF_STATUS_E_INVAL;
371 goto out;
372 }
373 status = qdf_validate_value(read_value);
374 if (QDF_IS_STATUS_ERROR(status)) {
375 status = QDF_STATUS_E_INVAL;
376 goto out;
377 }
378 }
379 }
380
381 out:
382 if (QDF_IS_STATUS_ERROR(status))
383 return false;
384 return true;
385 }
386
qdf_valid_ini_check(const char * ini_path)387 bool qdf_valid_ini_check(const char *ini_path)
388 {
389 QDF_STATUS status;
390 char *fbuf;
391 char *cursor;
392 bool is_valid = false;
393
394 if (qdf_str_eq(QDF_WIFI_MODULE_PARAMS_FILE, ini_path))
395 status = qdf_module_param_file_read(ini_path, &fbuf);
396 else
397 status = qdf_file_read(ini_path, &fbuf);
398 if (QDF_IS_STATUS_ERROR(status)) {
399 qdf_err("Failed to read *.ini file @ %s", ini_path);
400 return false;
401 }
402
403 /* foreach line */
404 cursor = fbuf;
405
406 is_valid = qdf_check_ini_validity(&cursor);
407
408 if (qdf_str_eq(QDF_WIFI_MODULE_PARAMS_FILE, ini_path))
409 qdf_module_param_file_free(fbuf);
410 else
411 qdf_file_buf_free(fbuf);
412
413 return is_valid;
414 }
415
416 qdf_export_symbol(qdf_valid_ini_check);
417