1/*
2 * Copyright 2004, Fran��ois Revol.
3 * Copyright 2007-2010, Axel D��rfler, axeld@pinc-software.de.
4 * Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de>
5 *
6 * Distributed under the terms of the MIT license.
7 */
8
9// TODO: this call is currently compiled for the kernel and libroot separately;
10//		they may not always return the same directory right now!
11
12#ifdef _KERNEL_MODE
13#	include <vfs.h>
14#else
15#	include <syscalls.h>
16#endif
17
18#include <directories.h>
19#include <FindDirectory.h>
20#include <fs_info.h>
21
22#include <errno.h>
23#include <pwd.h>
24#include <string.h>
25#include <sys/stat.h>
26#include <stdlib.h>
27#include <unistd.h>
28
29#include <errno_private.h>
30#include <user_group.h>
31
32/* use pwents to find home */
33#define USE_PWENTS
34
35
36/*
37 * If you change any of the directories below, please have a look at
38 * headers/private/libroot/directories.h and adjust that accordingly!
39 */
40
41#define SYSTEM "system"
42#define COMMON "common"
43#define NON_PACKAGED "/non-packaged"
44
45
46/* Haiku system directories */
47
48static const char *kSystemDirectories[] = {
49	SYSTEM,										// B_SYSTEM_DIRECTORY
50	SYSTEM,										// B_BEOS_SYSTEM_DIRECTORY
51	SYSTEM "/add-ons",
52	SYSTEM "/boot",
53	SYSTEM "/data/fonts",
54	SYSTEM "/lib",
55	SYSTEM "/servers",
56	SYSTEM "/apps",
57	SYSTEM "/bin",
58	COMMON "/etc",
59	SYSTEM "/documentation",
60	SYSTEM "/preferences",
61	SYSTEM "/add-ons/Translators",
62	SYSTEM "/add-ons/media",
63	SYSTEM "/data/sounds",
64	SYSTEM "/data",
65	"develop",
66	SYSTEM "/packages",
67	"develop/headers",
68};
69
70/* Common directories, shared among users */
71
72static const char *kCommonDirectories[] = {
73	COMMON,									// B_COMMON_DIRECTORY
74	COMMON,									// B_COMMON_SYSTEM_DIRECTORY
75	COMMON "/add-ons",
76	COMMON "/boot",
77	COMMON "/data/fonts",
78	COMMON "/lib",
79	COMMON "/servers",
80	COMMON "/bin",
81	COMMON "/etc",
82	COMMON "/documentation",
83	COMMON "/settings",
84	COMMON "/develop",						// B_COMMON_DEVELOP_DIRECTORY
85	COMMON "/var/log",						// B_COMMON_LOG_DIRECTORY
86	COMMON "/var/spool",					// B_COMMON_SPOOL_DIRECTORY
87	COMMON "/cache/tmp",					// B_COMMON_TEMP_DIRECTORY
88	COMMON "/var",							// B_COMMON_VAR_DIRECTORY
89	COMMON "/add-ons/Translators",
90	COMMON "/add-ons/media",
91	COMMON "/data/sounds",
92	COMMON "/data",
93	COMMON "/cache",						// B_COMMON_CACHE_DIRECTORY
94	COMMON "/packages",
95	COMMON "/include",
96	COMMON NON_PACKAGED,
97	COMMON NON_PACKAGED "/add-ons",
98	COMMON NON_PACKAGED "/add-ons/Translators",
99	COMMON NON_PACKAGED "/add-ons/media",
100	COMMON NON_PACKAGED "/bin",
101	COMMON NON_PACKAGED "/data",
102	COMMON NON_PACKAGED "/data/fonts",
103	COMMON NON_PACKAGED "/data/sounds",
104	COMMON NON_PACKAGED "/documentation",
105	COMMON NON_PACKAGED "/lib",
106	COMMON NON_PACKAGED "/develop/headers",
107};
108
109/* User directories */
110
111#define HOME "$h"
112#define CONFIG "/config"
113
114static const char *kUserDirectories[] = {
115	HOME,									// B_USER_DIRECTORY
116	HOME CONFIG,							// B_USER_CONFIG_DIRECTORY
117	HOME CONFIG "/add-ons",
118	HOME CONFIG "/boot",
119	HOME CONFIG "/data/fonts",
120	HOME CONFIG "/lib",
121	HOME CONFIG "/settings",
122	HOME CONFIG "/settings/deskbar",
123	HOME CONFIG "/settings/printers",
124	HOME CONFIG "/add-ons/Translators",
125	HOME CONFIG "/add-ons/media",
126	HOME CONFIG "/data/sounds",
127	HOME CONFIG "/data",
128	HOME CONFIG "/cache",
129	HOME CONFIG "/packages",
130	HOME CONFIG "/include",
131	HOME CONFIG NON_PACKAGED,
132	HOME CONFIG NON_PACKAGED "/add-ons",
133	HOME CONFIG NON_PACKAGED "/add-ons/Translators",
134	HOME CONFIG NON_PACKAGED "/add-ons/media",
135	HOME CONFIG NON_PACKAGED "/bin",
136	HOME CONFIG NON_PACKAGED "/data",
137	HOME CONFIG NON_PACKAGED "/data/fonts",
138	HOME CONFIG NON_PACKAGED "/data/sounds",
139	HOME CONFIG NON_PACKAGED "/documentation",
140	HOME CONFIG NON_PACKAGED "/lib",
141	HOME CONFIG NON_PACKAGED "/develop/headers",
142};
143
144
145/*! make dir and its parents if needed */
146static int
147create_path(const char *path, mode_t mode)
148{
149	char buffer[B_PATH_NAME_LENGTH + 1];
150	int pathLength;
151	int i = 0;
152
153	if (path == NULL || ((pathLength = strlen(path)) > B_PATH_NAME_LENGTH))
154		return EINVAL;
155
156	while (++i < pathLength) {
157		char *slash = strchr(&path[i], '/');
158		struct stat st;
159
160		if (slash == NULL)
161			i = pathLength;
162		else if (i != slash - path)
163			i = slash - path;
164		else
165			continue;
166
167		strlcpy(buffer, path, i + 1);
168		if (stat(buffer, &st) < 0) {
169			__set_errno(0);
170			if (mkdir(buffer, mode) < 0)
171				return errno;
172		}
173	}
174
175	return 0;
176}
177
178
179//	#pragma mark -
180
181
182status_t
183find_directory(directory_which which, dev_t device, bool createIt,
184	char *returnedPath, int32 pathLength)
185{
186	status_t err = B_OK;
187	dev_t bootDevice = -1;
188	struct fs_info fsInfo;
189	struct stat st;
190	char *buffer = NULL;
191	const char *home = NULL;
192	const char *templatePath = NULL;
193
194	/* as with the R5 version, no on-stack buffer */
195	buffer = (char *)malloc(pathLength);
196	memset(buffer, 0, pathLength);
197
198	/* fiddle with non-boot volume for items that need it */
199	switch (which) {
200		case B_DESKTOP_DIRECTORY:
201		case B_TRASH_DIRECTORY:
202			bootDevice = dev_for_path("/boot");
203			if (device <= 0)
204				device = bootDevice;
205			if (fs_stat_dev(device, &fsInfo) < B_OK) {
206				free(buffer);
207				return ENODEV;
208			}
209			if (device != bootDevice) {
210#ifdef _KERNEL_MODE
211				err = _user_entry_ref_to_path(device, fsInfo.root, /*"."*/
212					NULL, buffer, pathLength);
213#else
214				err = _kern_entry_ref_to_path(device, fsInfo.root, /*"."*/
215					NULL, buffer, pathLength);
216#endif
217			} else {
218				/* use the user id to find the home folder */
219				/* done later */
220				strlcat(buffer, "/boot", pathLength);
221			}
222			break;
223		case B_PACKAGE_LINKS_DIRECTORY:
224			// this is a directory living in rootfs
225			break;
226		default:
227			strlcat(buffer, "/boot", pathLength);
228			break;
229	}
230
231	if (err < B_OK) {
232		free(buffer);
233		return err;
234	}
235
236	switch (which) {
237		/* Per volume directories */
238		case B_DESKTOP_DIRECTORY:
239			if (device == bootDevice || !strcmp(fsInfo.fsh_name, "bfs"))
240				templatePath = "$h/Desktop";
241			break;
242		case B_TRASH_DIRECTORY:
243			// TODO: eventually put that into the file system API?
244			if (device == bootDevice || !strcmp(fsInfo.fsh_name, "bfs"))
245				templatePath = "trash"; // TODO: add suffix for current user
246			else if (!strcmp(fsInfo.fsh_name, "fat"))
247				templatePath = "RECYCLED/_BEOS_";
248			break;
249
250		/* Haiku system directories */
251		case B_SYSTEM_DIRECTORY:
252		case B_BEOS_SYSTEM_DIRECTORY:
253		case B_SYSTEM_ADDONS_DIRECTORY:
254		case B_SYSTEM_BOOT_DIRECTORY:
255		case B_SYSTEM_FONTS_DIRECTORY:
256		case B_SYSTEM_LIB_DIRECTORY:
257		case B_SYSTEM_SERVERS_DIRECTORY:
258		case B_SYSTEM_APPS_DIRECTORY:
259		case B_SYSTEM_BIN_DIRECTORY:
260		case B_BEOS_ETC_DIRECTORY:
261		case B_SYSTEM_DOCUMENTATION_DIRECTORY:
262		case B_SYSTEM_PREFERENCES_DIRECTORY:
263		case B_SYSTEM_TRANSLATORS_DIRECTORY:
264		case B_SYSTEM_MEDIA_NODES_DIRECTORY:
265		case B_SYSTEM_SOUNDS_DIRECTORY:
266		case B_SYSTEM_DATA_DIRECTORY:
267		case B_SYSTEM_DEVELOP_DIRECTORY:
268		case B_SYSTEM_PACKAGES_DIRECTORY:
269		case B_SYSTEM_HEADERS_DIRECTORY:
270			templatePath = kSystemDirectories[which - B_SYSTEM_DIRECTORY];
271			break;
272
273		/* Common directories, shared among users */
274		case B_COMMON_DIRECTORY:
275		case B_COMMON_SYSTEM_DIRECTORY:
276		case B_COMMON_ADDONS_DIRECTORY:
277		case B_COMMON_BOOT_DIRECTORY:
278		case B_COMMON_FONTS_DIRECTORY:
279		case B_COMMON_LIB_DIRECTORY:
280		case B_COMMON_SERVERS_DIRECTORY:
281		case B_COMMON_BIN_DIRECTORY:
282		case B_COMMON_ETC_DIRECTORY:
283		case B_COMMON_DOCUMENTATION_DIRECTORY:
284		case B_COMMON_SETTINGS_DIRECTORY:
285		case B_COMMON_DEVELOP_DIRECTORY:
286		case B_COMMON_LOG_DIRECTORY:
287		case B_COMMON_SPOOL_DIRECTORY:
288		case B_COMMON_TEMP_DIRECTORY:
289		case B_COMMON_VAR_DIRECTORY:
290		case B_COMMON_TRANSLATORS_DIRECTORY:
291		case B_COMMON_MEDIA_NODES_DIRECTORY:
292		case B_COMMON_SOUNDS_DIRECTORY:
293		case B_COMMON_DATA_DIRECTORY:
294		case B_COMMON_CACHE_DIRECTORY:
295		case B_COMMON_PACKAGES_DIRECTORY:
296		case B_COMMON_HEADERS_DIRECTORY:
297		case B_COMMON_NONPACKAGED_DIRECTORY:
298		case B_COMMON_NONPACKAGED_ADDONS_DIRECTORY:
299		case B_COMMON_NONPACKAGED_TRANSLATORS_DIRECTORY:
300		case B_COMMON_NONPACKAGED_MEDIA_NODES_DIRECTORY:
301		case B_COMMON_NONPACKAGED_BIN_DIRECTORY:
302		case B_COMMON_NONPACKAGED_DATA_DIRECTORY:
303		case B_COMMON_NONPACKAGED_FONTS_DIRECTORY:
304		case B_COMMON_NONPACKAGED_SOUNDS_DIRECTORY:
305		case B_COMMON_NONPACKAGED_DOCUMENTATION_DIRECTORY:
306		case B_COMMON_NONPACKAGED_LIB_DIRECTORY:
307		case B_COMMON_NONPACKAGED_HEADERS_DIRECTORY:
308			templatePath = kCommonDirectories[which - B_COMMON_DIRECTORY];
309			break;
310
311		/* User directories */
312		case B_USER_DIRECTORY:
313		case B_USER_CONFIG_DIRECTORY:
314		case B_USER_ADDONS_DIRECTORY:
315		case B_USER_BOOT_DIRECTORY:
316		case B_USER_FONTS_DIRECTORY:
317		case B_USER_LIB_DIRECTORY:
318		case B_USER_SETTINGS_DIRECTORY:
319		case B_USER_DESKBAR_DIRECTORY:
320		case B_USER_PRINTERS_DIRECTORY:
321		case B_USER_TRANSLATORS_DIRECTORY:
322		case B_USER_MEDIA_NODES_DIRECTORY:
323		case B_USER_SOUNDS_DIRECTORY:
324		case B_USER_DATA_DIRECTORY:
325		case B_USER_CACHE_DIRECTORY:
326		case B_USER_PACKAGES_DIRECTORY:
327		case B_USER_HEADERS_DIRECTORY:
328		case B_USER_NONPACKAGED_DIRECTORY:
329		case B_USER_NONPACKAGED_ADDONS_DIRECTORY:
330		case B_USER_NONPACKAGED_TRANSLATORS_DIRECTORY:
331		case B_USER_NONPACKAGED_MEDIA_NODES_DIRECTORY:
332		case B_USER_NONPACKAGED_BIN_DIRECTORY:
333		case B_USER_NONPACKAGED_DATA_DIRECTORY:
334		case B_USER_NONPACKAGED_FONTS_DIRECTORY:
335		case B_USER_NONPACKAGED_SOUNDS_DIRECTORY:
336		case B_USER_NONPACKAGED_DOCUMENTATION_DIRECTORY:
337		case B_USER_NONPACKAGED_LIB_DIRECTORY:
338		case B_USER_NONPACKAGED_HEADERS_DIRECTORY:
339			templatePath = kUserDirectories[which - B_USER_DIRECTORY];
340			break;
341
342		/* Global directories */
343		case B_APPS_DIRECTORY:
344			templatePath = "apps";
345			break;
346		case B_PREFERENCES_DIRECTORY:
347			templatePath = "preferences";
348			break;
349		case B_UTILITIES_DIRECTORY:
350			templatePath = "utilities";
351			break;
352		case B_PACKAGE_LINKS_DIRECTORY:
353			templatePath = "packages";
354			break;
355
356		default:
357			free(buffer);
358			return EINVAL;
359	}
360
361	err = B_OK;
362	if (templatePath) {
363		if (!strncmp(templatePath, "$h", 2)) {
364			if (bootDevice > -1 && device != bootDevice) {
365				int l = pathLength - strlen(buffer);
366				if (l > 5)
367					strncat(buffer, "/home", 5);
368			} else {
369#ifndef _KERNEL_MODE
370#ifdef USE_PWENTS
371				struct passwd pwBuffer;
372				char pwStringBuffer[MAX_PASSWD_BUFFER_SIZE];
373				struct passwd *pw;
374
375				if (getpwuid_r(geteuid(), &pwBuffer, pwStringBuffer,
376						sizeof(pwStringBuffer), &pw) == 0) {
377					home = pw->pw_dir;
378				}
379#endif	// USE_PWENTS
380				if (!home) {
381					/* use env var */
382					home = getenv("HOME");
383				}
384#endif	// !_KERNEL_MODE
385				if (!home)
386					home = kUserDirectory;
387				strncpy(buffer, home, pathLength);
388			}
389			templatePath += 2;
390		} else
391			strlcat(buffer, "/", pathLength);
392
393		if (!err && strlen(buffer) + 2 + strlen(templatePath)
394				< (uint32)pathLength) {
395			strcat(buffer, templatePath);
396		} else
397			err = err ? err : E2BIG;
398	} else
399		err = err ? err : ENOENT;
400
401	if (!err && createIt && stat(buffer, &st) < 0)
402		err = create_path(buffer, 0755);
403	if (!err)
404		strlcpy(returnedPath, buffer, pathLength);
405
406	free(buffer);
407	return err;
408}
409
410