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 				   &section_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 				   &section_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, &section_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