1/*
2 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "PackageSettings.h"
8
9#include <driver_settings.h>
10
11#include <AutoDeleterDrivers.h>
12#include <directories.h>
13#include <fs/KPath.h>
14#include <vfs.h>
15
16#include "DebugSupport.h"
17
18
19static const char* const kBlockedEntriesParameterName = "BlockedEntries";
20static const char* const kLegacyBlockedEntriesParameterName = "EntryBlacklist";
21
22
23// #pragma mark - PackageSettingsItem
24
25
26PackageSettingsItem::PackageSettingsItem()
27	:
28	fName(),
29	fEntries()
30{
31}
32
33
34PackageSettingsItem::~PackageSettingsItem()
35{
36	Entry* entry = fEntries.Clear(true);
37	while (entry != NULL) {
38		Entry* next = entry->HashNext();
39		delete entry;
40		entry = next;
41	}
42}
43
44
45status_t
46PackageSettingsItem::Init(const char* name)
47{
48	if (!fName.SetTo(name) || fEntries.Init() != B_OK)
49		RETURN_ERROR(B_NO_MEMORY);
50	return B_OK;
51}
52
53
54status_t
55PackageSettingsItem::ApplySettings(const driver_parameter* parameters,
56	int parameterCount)
57{
58	for (int i = 0; i < parameterCount; i++) {
59		const driver_parameter& subParameter = parameters[i];
60		if (strcmp(subParameter.name, kBlockedEntriesParameterName) != 0
61			&& strcmp(subParameter.name, kLegacyBlockedEntriesParameterName)
62				!= 0)
63			continue;
64
65		status_t error = _AddBlockedEntries(subParameter);
66		// abort only in case of serious issues (memory shortage)
67		if (error == B_NO_MEMORY)
68			return error;
69	}
70
71	return B_OK;
72}
73
74
75void
76PackageSettingsItem::AddEntry(Entry* entry)
77{
78	fEntries.Insert(entry);
79}
80
81
82status_t
83PackageSettingsItem::AddEntry(const char* path, Entry*& _entry)
84{
85	Entry* parent = NULL;
86
87	while (*path != '\0') {
88		while (*path == '/') {
89			path++;
90			continue;
91		}
92
93		const char* componentEnd = strchr(path, '/');
94		if (componentEnd == NULL)
95			componentEnd = path + strlen(path);
96
97		String name;
98		if (!name.SetTo(path, componentEnd - path))
99			RETURN_ERROR(B_NO_MEMORY);
100
101		Entry* entry = FindEntry(parent, name);
102		if (entry == NULL) {
103			entry = new(std::nothrow) Entry(parent, name);
104			if (entry == NULL)
105				RETURN_ERROR(B_NO_MEMORY);
106			AddEntry(entry);
107		}
108
109		path = componentEnd;
110		parent = entry;
111	}
112
113	if (parent == NULL)
114		return B_BAD_VALUE;
115
116	_entry = parent;
117	return B_OK;
118}
119
120
121PackageSettingsItem::Entry*
122PackageSettingsItem::FindEntry(Entry* parent, const String& name) const
123{
124	return fEntries.Lookup(EntryKey(parent, name));
125}
126
127
128PackageSettingsItem::Entry*
129PackageSettingsItem::FindEntry(Entry* parent, const char* name) const
130{
131	return fEntries.Lookup(EntryKey(parent, name));
132}
133
134
135status_t
136PackageSettingsItem::_AddBlockedEntries(const driver_parameter& parameter)
137{
138	for (int i = 0; i < parameter.parameter_count; i++) {
139		Entry* entry;
140		status_t error = AddEntry(parameter.parameters[i].name, entry);
141		// abort only in case of serious issues (memory shortage)
142		if (error == B_NO_MEMORY)
143			return error;
144
145		entry->SetBlocked(true);
146	}
147
148	return B_OK;
149}
150
151
152// #pragma mark - PackageSettings
153
154
155PackageSettings::PackageSettings()
156	:
157	fPackageItems()
158{
159}
160
161
162PackageSettings::~PackageSettings()
163{
164	PackageSettingsItem* item = fPackageItems.Clear(true);
165	while (item != NULL) {
166		PackageSettingsItem* next = item->HashNext();
167		delete item;
168		item = next;
169	}
170}
171
172
173status_t
174PackageSettings::Load(dev_t mountPointDeviceID, ino_t mountPointNodeID,
175	PackageFSMountType mountType)
176{
177	status_t error = fPackageItems.Init();
178	if (error != B_OK)
179		RETURN_ERROR(error);
180
181	// First get the safe mode options. Those apply to the system package.
182	if (mountType == PACKAGE_FS_MOUNT_TYPE_SYSTEM) {
183		void* settingsHandle = load_driver_settings(B_SAFEMODE_DRIVER_SETTINGS);
184		if (settingsHandle != NULL) {
185			if (const driver_settings* settings
186					= get_driver_settings(settingsHandle)) {
187				error = _AddPackageSettingsItem("haiku", settings->parameters,
188					settings->parameter_count);
189				// abort only in case of serious issues (memory shortage)
190				if (error == B_NO_MEMORY)
191					return error;
192			}
193			unload_driver_settings(settingsHandle);
194		}
195	}
196
197	// get the mount point relative settings file path
198	const char* settingsFilePath = mountType == PACKAGE_FS_MOUNT_TYPE_HOME
199		? &(kUserSettingsGlobalDirectory "/packages")
200			[strlen(kUserConfigDirectory) + 1]
201		: &(kSystemSettingsDirectory "/packages")[strlen(kSystemDirectory) + 1];
202
203	// get an absolute path
204	KPath path;
205	if (path.InitCheck() != B_OK)
206		RETURN_ERROR(path.InitCheck());
207
208	error = vfs_entry_ref_to_path(mountPointDeviceID, mountPointNodeID,
209		NULL, true, path.LockBuffer(), path.BufferSize());
210	if (error != B_OK)
211		return error;
212	path.UnlockBuffer();
213
214	error = path.Append(settingsFilePath);
215	if (error != B_OK)
216		return error;
217
218	// load the driver settings
219	DriverSettingsUnloader settingsHandle(load_driver_settings(path.Path()));
220	if (!settingsHandle.IsSet())
221		return B_ENTRY_NOT_FOUND;
222
223	const driver_settings* settings
224		= get_driver_settings(settingsHandle.Get());
225	for (int i = 0; i < settings->parameter_count; i++) {
226		const driver_parameter& parameter = settings->parameters[i];
227		if (strcmp(parameter.name, "Package") != 0
228			|| parameter.value_count < 1) {
229			continue;
230		}
231
232		error = _AddPackageSettingsItem(parameter.values[0],
233			parameter.parameters, parameter.parameter_count);
234		// abort only in case of serious issues (memory shortage)
235		if (error == B_NO_MEMORY)
236			return error;
237	}
238
239	return B_OK;
240}
241
242
243const PackageSettingsItem*
244PackageSettings::PackageItemFor(const String& name) const
245{
246	return fPackageItems.Lookup(name);
247}
248
249
250status_t
251PackageSettings::_AddPackageSettingsItem(const char* name,
252	const driver_parameter* parameters, int parameterCount)
253{
254	// get/create the package item
255	PackageSettingsItem* packageItem = fPackageItems.Lookup(StringKey(name));
256	if (packageItem == NULL) {
257		packageItem = new(std::nothrow) PackageSettingsItem;
258		if (packageItem == NULL || packageItem->Init(name) != B_OK) {
259			delete packageItem;
260			RETURN_ERROR(B_NO_MEMORY);
261		}
262
263		fPackageItems.Insert(packageItem);
264	}
265
266	// apply the settings
267	return packageItem->ApplySettings(parameters, parameterCount);
268}
269