1/*
2* Copyright (c) 2024 Huawei Device Co., Ltd.
3* Licensed under the Apache License, Version 2.0 (the "License");
4* you may not use this file except in compliance with the License.
5* You may obtain a copy of the License at
6*
7*     http://www.apache.org/licenses/LICENSE-2.0
8*
9* Unless required by applicable law or agreed to in writing, software
10* distributed under the License is distributed on an "AS IS" BASIS,
11* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12* See the License for the specific language governing permissions and
13* limitations under the License.
14*/
15
16/* Skipping relabel path if define in ignore_cfg. */
17#include <errno.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21#include <stdbool.h>
22
23#include "ignore_path.h"
24#include "callbacks.h"
25
26static g_system_config_loaded = false;
27static g_vendor_config_loaded = false;
28ignore_paths_t g_ignore_paths = {NULL, NULL};
29
30ignore_path_node_t *insert_ignore_path(ignore_path_node_t **paths_ptr, const char *line)
31{
32	ignore_path_node_t *new_node = malloc(sizeof(ignore_path_node_t));
33	if (new_node == NULL) {
34		return NULL;
35	}
36	new_node->path = strdup(line);
37	if (new_node->path == NULL) {
38		free(new_node);
39		return NULL;
40	}
41	new_node->next = *paths_ptr;
42	*paths_ptr = new_node;
43	return new_node;
44}
45
46size_t trim_newline(char *str)
47{
48	size_t length = strlen(str);
49	if (length == 0) {
50		return 0;
51	}
52	if (str[length - 1] == '\n') {
53		str[--length] = '\0';
54	}
55	if (length > 0 && str[length - 1] == '\r') {
56		str[--length] = '\0';
57	}
58	return length;
59}
60
61path_info_t trim_suffix_and_get_path_info(char *line, size_t real_length)
62{
63	path_info_t info;
64	if (line[real_length - SLASH_SUFFIX_LEN] == '/') {
65		info.paths_ptr = &g_ignore_paths.slash_suffix_paths;
66		info.suffix_len = SLASH_SUFFIX_LEN;
67	} else if (real_length > 1
68				&& line[real_length - SLASH_SUFFIX_LEN] == '*'
69				&& line[real_length - STAR_SUFFIX_LEN] == '/') {
70		info.paths_ptr = &g_ignore_paths.star_suffix_paths;
71		info.suffix_len = STAR_SUFFIX_LEN;
72	} else {
73		info.paths_ptr = NULL;
74		info.suffix_len = 0;
75	}
76	if (info.paths_ptr != NULL) {
77		line[real_length - info.suffix_len] = '\0';
78	}
79	return info;
80}
81
82bool load_ignore_cfg_from_file(const char *cfg_path)
83{
84	FILE *file = fopen(cfg_path, "r");
85	if (file == NULL) {
86		selinux_log(SELINUX_INFO, "Failed to open file: %s\n", cfg_path);
87		return false;
88	}
89
90	char *line = NULL;
91	size_t len = 0;
92	bool result = true;
93	while (getline(&line, &len, file) != -1) {
94		size_t real_length = trim_newline(line);
95		if (real_length > 0) {
96			path_info_t info = trim_suffix_and_get_path_info(line, real_length);
97			if (info.paths_ptr == NULL) {
98				continue;
99			}
100			line[real_length - info.suffix_len] = '\0';
101			ignore_path_node_t *new_node = insert_ignore_path(info.paths_ptr, line);
102			if (new_node == NULL) {
103				selinux_log(SELINUX_ERROR, "Failed to allocate memory or duplicate string: %s\n", line);
104				continue;
105			}
106		}
107	}
108
109	if (fclose(file)) {
110		selinux_log(SELINUX_ERROR, "Failed to close file: %s\n", strerror(errno));
111	}
112	free(line);
113	return result;
114}
115
116bool load_ignore_cfg()
117{
118	if (g_system_config_loaded && g_vendor_config_loaded) {
119		return true;
120	}
121	bool result = true;
122	if (!g_system_config_loaded) {
123		g_system_config_loaded = load_ignore_cfg_from_file(SYSTEM_IGNORE_CFG_PATH);
124	}
125	if (!g_vendor_config_loaded) {
126		g_vendor_config_loaded = load_ignore_cfg_from_file(VENDOR_IGNORE_CFG_PATH);
127	}
128	if (!g_system_config_loaded || !g_vendor_config_loaded) {
129		result = false;
130	}
131	return result;
132}
133
134static bool find_ignore_path(ignore_path_node_t *current, const char *path)
135{
136	while (current != NULL) {
137		if (strcmp(path, current->path) == 0) {
138			return true;
139		}
140		current = current->next;
141	}
142	return false;
143}
144
145enum skip_type skip_ignore_relabel(const char *path)
146{
147	if (find_ignore_path(g_ignore_paths.slash_suffix_paths, path)) {
148		return SKIP_SELF_SUB_DIR;
149	}
150	if (find_ignore_path(g_ignore_paths.star_suffix_paths, path)) {
151		return SKIP_SUB_DIR;
152	}
153	return SKIP_NONE;
154}
155
156void free_ignore_list(ignore_path_node_t **list_ptr)
157{
158	while (*list_ptr != NULL) {
159		ignore_path_node_t *temp = *list_ptr;
160		*list_ptr = (*list_ptr)->next;
161		free(temp->path);
162		free(temp);
163	}
164	*list_ptr = NULL;
165}
166
167void free_ignore_cfg()
168{
169	free_ignore_list(&g_ignore_paths.slash_suffix_paths);
170	free_ignore_list(&g_ignore_paths.star_suffix_paths);
171	g_system_config_loaded = false;
172	g_vendor_config_loaded = false;
173}
174