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 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
6 *
7 * Distributed under the terms of the MIT license.
8 */
9
10// TODO: this call is currently compiled for the kernel and libroot separately;
11//		they may not always return the same directory right now!
12
13#ifdef _KERNEL_MODE
14#	include <vfs.h>
15#else
16#	include <syscalls.h>
17#endif
18
19#include <directories.h>
20#include <FindDirectory.h>
21#include <fs_info.h>
22#include <StackOrHeapArray.h>
23
24#include <errno.h>
25#include <pwd.h>
26#include <string.h>
27#include <sys/stat.h>
28#include <stdlib.h>
29#include <unistd.h>
30
31#include <architecture_private.h>
32#include <errno_private.h>
33#include <find_directory_private.h>
34#include <stdlib_private.h>
35#include <symbol_versioning.h>
36#include <user_group.h>
37
38#include <AutoDeleter.h>
39
40#include "PathBuffer.h"
41
42
43/* use pwents to find home */
44#define USE_PWENTS
45
46
47/*
48 * If you change any of the directories below, please have a look at
49 * headers/private/libroot/directories.h and adjust that accordingly!
50 */
51
52#define SYSTEM "system"
53#define COMMON "system/data/empty"
54#define NON_PACKAGED "/non-packaged"
55
56enum {
57	// obsolete common directories
58	B_COMMON_DIRECTORY					= 2000,
59	B_COMMON_SYSTEM_DIRECTORY,
60	B_COMMON_ADDONS_DIRECTORY,
61	B_COMMON_BOOT_DIRECTORY,
62	B_COMMON_FONTS_DIRECTORY,
63	B_COMMON_LIB_DIRECTORY,
64	B_COMMON_SERVERS_DIRECTORY,
65	B_COMMON_BIN_DIRECTORY,
66	_B_COMMON_ETC_DIRECTORY,
67	B_COMMON_DOCUMENTATION_DIRECTORY,
68	_B_COMMON_SETTINGS_DIRECTORY,
69	B_COMMON_DEVELOP_DIRECTORY,
70	_B_COMMON_LOG_DIRECTORY,
71	_B_COMMON_SPOOL_DIRECTORY,
72	_B_COMMON_TEMP_DIRECTORY,
73	_B_COMMON_VAR_DIRECTORY,
74	B_COMMON_TRANSLATORS_DIRECTORY,
75	B_COMMON_MEDIA_NODES_DIRECTORY,
76	B_COMMON_SOUNDS_DIRECTORY,
77	B_COMMON_DATA_DIRECTORY,
78	_B_COMMON_CACHE_DIRECTORY,
79	B_COMMON_PACKAGES_DIRECTORY,
80	B_COMMON_HEADERS_DIRECTORY,
81};
82
83
84/* Haiku system directories */
85
86static const char *kSystemDirectories[] = {
87	SYSTEM,										// B_SYSTEM_DIRECTORY
88	SYSTEM,										// B_BEOS_SYSTEM_DIRECTORY
89	SYSTEM "/add-ons$a",
90	SYSTEM "/boot",
91	SYSTEM "/data/fonts",
92	SYSTEM "/lib$a",
93	SYSTEM "/servers",
94	SYSTEM "/apps",
95	SYSTEM "/bin$a",
96	SYSTEM "/settings/etc",
97	SYSTEM "/documentation",
98	SYSTEM "/preferences",
99	SYSTEM "/add-ons$a/Translators",
100	SYSTEM "/add-ons$a/media",
101	SYSTEM "/data/sounds",
102	SYSTEM "/data",
103	SYSTEM "/develop",
104	SYSTEM "/packages",
105	SYSTEM "/develop/headers$a",
106	SYSTEM "/data/deskbar/menu",
107};
108
109/* Common directories, shared among users */
110
111static const char *kCommonDirectories[] = {
112	COMMON,									// B_COMMON_DIRECTORY
113	COMMON,									// B_COMMON_SYSTEM_DIRECTORY
114	COMMON "/add-ons$a",
115	COMMON "/boot",
116	COMMON "/data/fonts",
117	COMMON "/lib$a",
118	COMMON "/servers",
119	COMMON "/bin$a",
120	SYSTEM "/settings/etc",					// B_SYSTEM_ETC_DIRECTORY
121	COMMON "/documentation",
122	SYSTEM "/settings",						// B_SYSTEM_SETTINGS_DIRECTORY
123	COMMON "/develop",
124	SYSTEM "/var/log",						// B_SYSTEM_LOG_DIRECTORY
125	SYSTEM "/var/spool",					// B_SYSTEM_SPOOL_DIRECTORY
126	SYSTEM "/cache/tmp",					// B_SYSTEM_TEMP_DIRECTORY
127	SYSTEM "/var",							// B_SYSTEM_VAR_DIRECTORY
128	COMMON "/add-ons$a/Translators",
129	COMMON "/add-ons$a/media",
130	COMMON "/data/sounds",
131	COMMON "/data",
132	SYSTEM "/cache",						// B_SYSTEM_CACHE_DIRECTORY
133	COMMON "/packages",
134	COMMON "/develop/headers$a",
135	SYSTEM NON_PACKAGED,
136	SYSTEM NON_PACKAGED "/add-ons$a",
137	SYSTEM NON_PACKAGED "/add-ons$a/Translators",
138	SYSTEM NON_PACKAGED "/add-ons$a/media",
139	SYSTEM NON_PACKAGED "/bin$a",
140	SYSTEM NON_PACKAGED "/data",
141	SYSTEM NON_PACKAGED "/data/fonts",
142	SYSTEM NON_PACKAGED "/data/sounds",
143	SYSTEM NON_PACKAGED "/documentation",
144	SYSTEM NON_PACKAGED "/lib$a",
145	SYSTEM NON_PACKAGED "/develop/headers$a",
146	SYSTEM NON_PACKAGED "/develop",
147};
148
149/* User directories */
150
151#define HOME "$h"
152#define CONFIG "/config"
153
154static const char *kUserDirectories[] = {
155	HOME,									// B_USER_DIRECTORY
156	HOME CONFIG,							// B_USER_CONFIG_DIRECTORY
157	HOME CONFIG "/add-ons$a",
158	HOME CONFIG "/settings/boot",
159	HOME CONFIG "/data/fonts",
160	HOME CONFIG "/lib$a",
161	HOME CONFIG "/settings",
162	HOME CONFIG "/settings/deskbar/menu",
163	HOME CONFIG "/settings/printers",
164	HOME CONFIG "/add-ons$a/Translators",
165	HOME CONFIG "/add-ons$a/media",
166	HOME CONFIG "/data/sounds",
167	HOME CONFIG "/data",
168	HOME CONFIG "/cache",
169	HOME CONFIG "/packages",
170	HOME CONFIG "/develop/headers$a",
171	HOME CONFIG NON_PACKAGED,
172	HOME CONFIG NON_PACKAGED "/add-ons$a",
173	HOME CONFIG NON_PACKAGED "/add-ons$a/Translators",
174	HOME CONFIG NON_PACKAGED "/add-ons$a/media",
175	HOME CONFIG NON_PACKAGED "/bin$a",
176	HOME CONFIG NON_PACKAGED "/data",
177	HOME CONFIG NON_PACKAGED "/data/fonts",
178	HOME CONFIG NON_PACKAGED "/data/sounds",
179	HOME CONFIG NON_PACKAGED "/documentation",
180	HOME CONFIG NON_PACKAGED "/lib$a",
181	HOME CONFIG NON_PACKAGED "/develop/headers$a",
182	HOME CONFIG NON_PACKAGED "/develop",
183	HOME CONFIG "/develop",
184	HOME CONFIG "/documentation",
185	HOME CONFIG "/servers",
186	HOME CONFIG "/apps",
187	HOME CONFIG "/bin$a",
188	HOME CONFIG "/preferences",
189	HOME CONFIG "/settings/etc",
190	HOME CONFIG "/var/log",
191	HOME CONFIG "/var/spool",
192	HOME CONFIG "/var",
193};
194
195#ifndef _LOADER_MODE
196/*! make dir and its parents if needed */
197static int
198create_path(const char *path, mode_t mode)
199{
200	int pathLength;
201	int i = 0;
202
203	if (path == NULL || ((pathLength = strlen(path)) > B_PATH_NAME_LENGTH))
204		return EINVAL;
205
206	BStackOrHeapArray<char, 128> buffer(pathLength + 1);
207	if (!buffer.IsValid())
208		return B_NO_MEMORY;
209
210	while (++i < pathLength) {
211		char *slash = strchr(&path[i], '/');
212		struct stat st;
213
214		if (slash == NULL)
215			i = pathLength;
216		else if (i != slash - path)
217			i = slash - path;
218		else
219			continue;
220
221		strlcpy(buffer, path, i + 1);
222		if (stat(buffer, &st) < 0) {
223			__set_errno(0);
224			if (mkdir(buffer, mode) < 0)
225				return errno;
226		}
227	}
228
229	return 0;
230}
231
232
233static size_t
234get_user_home_path(char* buffer, size_t bufferSize)
235{
236	const char* home = NULL;
237#ifndef _KERNEL_MODE
238#ifdef USE_PWENTS
239	uid_t user = geteuid();
240	if (user == 0) {
241		// TODO: this is a work-around as the launch_daemon, and the registrar
242		// must not call getpwuid_r().
243		return strlcpy(buffer, kUserDirectory, bufferSize);
244	}
245
246	struct passwd pwBuffer;
247	char pwStringBuffer[MAX_PASSWD_BUFFER_SIZE];
248	struct passwd* pw;
249
250	if (getpwuid_r(user, &pwBuffer, pwStringBuffer,
251			sizeof(pwStringBuffer), &pw) == 0
252		&& pw != NULL) {
253		home = pw->pw_dir;
254	}
255#endif	// USE_PWENTS
256	if (home == NULL) {
257		/* use env var */
258		ssize_t result = __getenv_reentrant("HOME", buffer, bufferSize);
259		if (result >= 0)
260			return result;
261	}
262#endif	// !_KERNEL_MODE
263	if (home == NULL)
264		home = kUserDirectory;
265
266	return strlcpy(buffer, home, bufferSize);
267}
268
269
270//	#pragma mark -
271
272
273status_t
274__find_directory(directory_which which, dev_t device, bool createIt,
275	char *returnedPath, int32 _pathLength)
276{
277	if (_pathLength <= 0)
278		return E2BIG;
279	size_t pathLength = _pathLength;
280
281	status_t err = B_OK;
282	dev_t bootDevice = -1;
283	struct fs_info fsInfo;
284	struct stat st;
285	const char *templatePath = NULL;
286
287	/* as with the R5 version, no on-stack buffer */
288	char *buffer = (char*)malloc(pathLength);
289	if (buffer == NULL)
290		return B_NO_MEMORY;
291	MemoryDeleter bufferDeleter(buffer);
292
293	memset(buffer, 0, pathLength);
294
295	/* fiddle with non-boot volume for items that need it */
296	switch (which) {
297		case B_DESKTOP_DIRECTORY:
298		case B_TRASH_DIRECTORY:
299			bootDevice = dev_for_path("/boot");
300			if (device <= 0)
301				device = bootDevice;
302			if (fs_stat_dev(device, &fsInfo) != B_OK)
303				return ENODEV;
304			if (device != bootDevice) {
305#ifdef _KERNEL_MODE
306				err = _user_entry_ref_to_path(device, fsInfo.root, /*"."*/
307					NULL, buffer, pathLength);
308#else
309				err = _kern_entry_ref_to_path(device, fsInfo.root, /*"."*/
310					NULL, buffer, pathLength);
311#endif
312				if (err != B_OK)
313					return err;
314			} else {
315				/* use the user id to find the home folder */
316				/* done later */
317				strlcat(buffer, "/boot", pathLength);
318			}
319			break;
320		case B_PACKAGE_LINKS_DIRECTORY:
321			// this is a directory living in rootfs
322			break;
323		default:
324			strlcat(buffer, "/boot", pathLength);
325			break;
326	}
327
328	switch ((int)which) {
329		/* Per volume directories */
330		case B_DESKTOP_DIRECTORY:
331			if (device == bootDevice || !strcmp(fsInfo.fsh_name, "bfs"))
332				templatePath = "$h/Desktop";
333			break;
334		case B_TRASH_DIRECTORY:
335			// TODO: eventually put that into the file system API?
336			if (device == bootDevice || !strcmp(fsInfo.fsh_name, "bfs"))
337				templatePath = "trash"; // TODO: add suffix for current user
338			else if (!strcmp(fsInfo.fsh_name, "fat"))
339				templatePath = "RECYCLED/_BEOS_";
340			break;
341
342		/* Haiku system directories */
343		case B_SYSTEM_DIRECTORY:
344		case B_BEOS_SYSTEM_DIRECTORY:
345		case B_SYSTEM_ADDONS_DIRECTORY:
346		case B_SYSTEM_BOOT_DIRECTORY:
347		case B_SYSTEM_FONTS_DIRECTORY:
348		case B_SYSTEM_LIB_DIRECTORY:
349		case B_SYSTEM_SERVERS_DIRECTORY:
350		case B_SYSTEM_APPS_DIRECTORY:
351		case B_SYSTEM_BIN_DIRECTORY:
352		case B_BEOS_ETC_DIRECTORY:
353		case B_SYSTEM_DOCUMENTATION_DIRECTORY:
354		case B_SYSTEM_PREFERENCES_DIRECTORY:
355		case B_SYSTEM_TRANSLATORS_DIRECTORY:
356		case B_SYSTEM_MEDIA_NODES_DIRECTORY:
357		case B_SYSTEM_SOUNDS_DIRECTORY:
358		case B_SYSTEM_DATA_DIRECTORY:
359		case B_SYSTEM_DEVELOP_DIRECTORY:
360		case B_SYSTEM_PACKAGES_DIRECTORY:
361		case B_SYSTEM_HEADERS_DIRECTORY:
362		case B_SYSTEM_DESKBAR_DIRECTORY:
363			templatePath = kSystemDirectories[which - B_SYSTEM_DIRECTORY];
364			break;
365
366		/* Obsolete common directories and writable system directories */
367		case B_COMMON_DIRECTORY:
368		case B_COMMON_SYSTEM_DIRECTORY:
369		case B_COMMON_ADDONS_DIRECTORY:
370		case B_COMMON_BOOT_DIRECTORY:
371		case B_COMMON_FONTS_DIRECTORY:
372		case B_COMMON_LIB_DIRECTORY:
373		case B_COMMON_SERVERS_DIRECTORY:
374		case B_COMMON_BIN_DIRECTORY:
375		case B_SYSTEM_ETC_DIRECTORY:
376		case B_COMMON_DOCUMENTATION_DIRECTORY:
377		case B_SYSTEM_SETTINGS_DIRECTORY:
378		case B_COMMON_DEVELOP_DIRECTORY:
379		case B_SYSTEM_LOG_DIRECTORY:
380		case B_SYSTEM_SPOOL_DIRECTORY:
381		case B_SYSTEM_TEMP_DIRECTORY:
382		case B_SYSTEM_VAR_DIRECTORY:
383		case B_COMMON_TRANSLATORS_DIRECTORY:
384		case B_COMMON_MEDIA_NODES_DIRECTORY:
385		case B_COMMON_SOUNDS_DIRECTORY:
386		case B_COMMON_DATA_DIRECTORY:
387		case B_SYSTEM_CACHE_DIRECTORY:
388		case B_COMMON_PACKAGES_DIRECTORY:
389		case B_COMMON_HEADERS_DIRECTORY:
390		case B_SYSTEM_NONPACKAGED_DIRECTORY:
391		case B_SYSTEM_NONPACKAGED_ADDONS_DIRECTORY:
392		case B_SYSTEM_NONPACKAGED_TRANSLATORS_DIRECTORY:
393		case B_SYSTEM_NONPACKAGED_MEDIA_NODES_DIRECTORY:
394		case B_SYSTEM_NONPACKAGED_BIN_DIRECTORY:
395		case B_SYSTEM_NONPACKAGED_DATA_DIRECTORY:
396		case B_SYSTEM_NONPACKAGED_FONTS_DIRECTORY:
397		case B_SYSTEM_NONPACKAGED_SOUNDS_DIRECTORY:
398		case B_SYSTEM_NONPACKAGED_DOCUMENTATION_DIRECTORY:
399		case B_SYSTEM_NONPACKAGED_LIB_DIRECTORY:
400		case B_SYSTEM_NONPACKAGED_HEADERS_DIRECTORY:
401		case B_SYSTEM_NONPACKAGED_DEVELOP_DIRECTORY:
402			templatePath = kCommonDirectories[which - B_COMMON_DIRECTORY];
403			break;
404
405		/* User directories */
406		case B_USER_DIRECTORY:
407		case B_USER_CONFIG_DIRECTORY:
408		case B_USER_ADDONS_DIRECTORY:
409		case B_USER_BOOT_DIRECTORY:
410		case B_USER_FONTS_DIRECTORY:
411		case B_USER_LIB_DIRECTORY:
412		case B_USER_SETTINGS_DIRECTORY:
413		case B_USER_DESKBAR_DIRECTORY:
414		case B_USER_PRINTERS_DIRECTORY:
415		case B_USER_TRANSLATORS_DIRECTORY:
416		case B_USER_MEDIA_NODES_DIRECTORY:
417		case B_USER_SOUNDS_DIRECTORY:
418		case B_USER_DATA_DIRECTORY:
419		case B_USER_CACHE_DIRECTORY:
420		case B_USER_PACKAGES_DIRECTORY:
421		case B_USER_HEADERS_DIRECTORY:
422		case B_USER_DEVELOP_DIRECTORY:
423		case B_USER_DOCUMENTATION_DIRECTORY:
424		case B_USER_NONPACKAGED_DIRECTORY:
425		case B_USER_NONPACKAGED_ADDONS_DIRECTORY:
426		case B_USER_NONPACKAGED_TRANSLATORS_DIRECTORY:
427		case B_USER_NONPACKAGED_MEDIA_NODES_DIRECTORY:
428		case B_USER_NONPACKAGED_BIN_DIRECTORY:
429		case B_USER_NONPACKAGED_DATA_DIRECTORY:
430		case B_USER_NONPACKAGED_FONTS_DIRECTORY:
431		case B_USER_NONPACKAGED_SOUNDS_DIRECTORY:
432		case B_USER_NONPACKAGED_DOCUMENTATION_DIRECTORY:
433		case B_USER_NONPACKAGED_LIB_DIRECTORY:
434		case B_USER_NONPACKAGED_HEADERS_DIRECTORY:
435		case B_USER_NONPACKAGED_DEVELOP_DIRECTORY:
436		case B_USER_SERVERS_DIRECTORY:
437		case B_USER_APPS_DIRECTORY:
438		case B_USER_BIN_DIRECTORY:
439		case B_USER_PREFERENCES_DIRECTORY:
440		case B_USER_ETC_DIRECTORY:
441		case B_USER_LOG_DIRECTORY:
442		case B_USER_SPOOL_DIRECTORY:
443		case B_USER_VAR_DIRECTORY:
444			templatePath = kUserDirectories[which - B_USER_DIRECTORY];
445			break;
446
447		/* Global directories */
448		case B_APPS_DIRECTORY:
449		case B_UTILITIES_DIRECTORY:
450			templatePath = SYSTEM "/apps";
451			break;
452		case B_PREFERENCES_DIRECTORY:
453			templatePath = SYSTEM "/preferences";
454			break;
455		case B_PACKAGE_LINKS_DIRECTORY:
456			templatePath = "packages";
457			break;
458
459		default:
460			return EINVAL;
461	}
462
463	if (templatePath == NULL)
464		return ENOENT;
465
466	PathBuffer pathBuffer(buffer, pathLength, strlen(buffer));
467
468	// resolve "$h" placeholder to the user's home directory
469	if (!strncmp(templatePath, "$h", 2)) {
470		if (bootDevice > -1 && device != bootDevice) {
471			pathBuffer.Append("/home");
472		} else {
473			size_t length = get_user_home_path(buffer, pathLength);
474			if (length >= pathLength)
475				return E2BIG;
476			pathBuffer.SetTo(buffer, pathLength, length);
477		}
478		templatePath += 2;
479	} else if (templatePath[0] != '\0')
480		pathBuffer.Append('/');
481
482	// resolve "$a" placeholder to the architecture subdirectory, if not
483	// primary
484	if (char* dollar = strchr(templatePath, '$')) {
485		if (dollar[1] == 'a') {
486			pathBuffer.Append(templatePath, dollar - templatePath);
487#ifndef _KERNEL_MODE
488			const char* architecture = __get_architecture();
489			if (strcmp(architecture, __get_primary_architecture()) != 0) {
490				pathBuffer.Append('/');
491				pathBuffer.Append(architecture);
492			}
493#endif
494			templatePath = dollar + 2;
495		}
496	}
497
498	// append (remainder of) template path
499	pathBuffer.Append(templatePath);
500
501	if (pathBuffer.Length() >= pathLength)
502		return E2BIG;
503
504	if (createIt && stat(buffer, &st) < 0) {
505		err = create_path(buffer, 0755);
506		if (err != B_OK)
507			return err;
508	}
509
510	strlcpy(returnedPath, buffer, pathLength);
511	return B_OK;
512}
513
514
515extern "C" status_t
516__find_directory_alpha4(directory_which which, dev_t device, bool createIt,
517	char *returnedPath, int32 pathLength)
518{
519	return __find_directory(which, device, createIt, returnedPath, pathLength);
520}
521
522
523DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__find_directory_alpha4",
524	"find_directory@", "BASE");
525
526DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__find_directory", "find_directory@@",
527	"1_ALPHA5");
528#else // _LOADER_MODE
529status_t
530__find_directory(directory_which which, dev_t device, bool createIt,
531	char *returnedPath, int32 _pathLength)
532{
533	if (_pathLength <= 0)
534		return E2BIG;
535	size_t pathLength = _pathLength;
536
537	const char *templatePath = NULL;
538
539	/* as with the R5 version, no on-stack buffer */
540	char *buffer = (char*)malloc(pathLength);
541	if (buffer == NULL)
542		return B_NO_MEMORY;
543	MemoryDeleter bufferDeleter(buffer);
544
545	memset(buffer, 0, pathLength);
546
547	strlcat(buffer, "/boot", pathLength);
548
549	switch ((int)which) {
550		/* Haiku system directories */
551		case B_SYSTEM_DIRECTORY:
552		case B_BEOS_SYSTEM_DIRECTORY:
553		case B_SYSTEM_ADDONS_DIRECTORY:
554		case B_SYSTEM_BOOT_DIRECTORY:
555		case B_SYSTEM_FONTS_DIRECTORY:
556		case B_SYSTEM_LIB_DIRECTORY:
557		case B_SYSTEM_SERVERS_DIRECTORY:
558		case B_SYSTEM_APPS_DIRECTORY:
559		case B_SYSTEM_BIN_DIRECTORY:
560		case B_BEOS_ETC_DIRECTORY:
561		case B_SYSTEM_DOCUMENTATION_DIRECTORY:
562		case B_SYSTEM_PREFERENCES_DIRECTORY:
563		case B_SYSTEM_TRANSLATORS_DIRECTORY:
564		case B_SYSTEM_MEDIA_NODES_DIRECTORY:
565		case B_SYSTEM_SOUNDS_DIRECTORY:
566		case B_SYSTEM_DATA_DIRECTORY:
567		case B_SYSTEM_DEVELOP_DIRECTORY:
568		case B_SYSTEM_PACKAGES_DIRECTORY:
569		case B_SYSTEM_HEADERS_DIRECTORY:
570		case B_SYSTEM_DESKBAR_DIRECTORY:
571			templatePath = kSystemDirectories[which - B_SYSTEM_DIRECTORY];
572			break;
573
574		/* Obsolete common directories and writable system directories */
575		case B_COMMON_DIRECTORY:
576		case B_COMMON_SYSTEM_DIRECTORY:
577		case B_COMMON_ADDONS_DIRECTORY:
578		case B_COMMON_BOOT_DIRECTORY:
579		case B_COMMON_FONTS_DIRECTORY:
580		case B_COMMON_LIB_DIRECTORY:
581		case B_COMMON_SERVERS_DIRECTORY:
582		case B_COMMON_BIN_DIRECTORY:
583		case B_SYSTEM_ETC_DIRECTORY:
584		case B_COMMON_DOCUMENTATION_DIRECTORY:
585		case B_SYSTEM_SETTINGS_DIRECTORY:
586		case B_COMMON_DEVELOP_DIRECTORY:
587		case B_SYSTEM_LOG_DIRECTORY:
588		case B_SYSTEM_SPOOL_DIRECTORY:
589		case B_SYSTEM_TEMP_DIRECTORY:
590		case B_SYSTEM_VAR_DIRECTORY:
591		case B_COMMON_TRANSLATORS_DIRECTORY:
592		case B_COMMON_MEDIA_NODES_DIRECTORY:
593		case B_COMMON_SOUNDS_DIRECTORY:
594		case B_COMMON_DATA_DIRECTORY:
595		case B_SYSTEM_CACHE_DIRECTORY:
596		case B_COMMON_PACKAGES_DIRECTORY:
597		case B_COMMON_HEADERS_DIRECTORY:
598		case B_SYSTEM_NONPACKAGED_DIRECTORY:
599		case B_SYSTEM_NONPACKAGED_ADDONS_DIRECTORY:
600		case B_SYSTEM_NONPACKAGED_TRANSLATORS_DIRECTORY:
601		case B_SYSTEM_NONPACKAGED_MEDIA_NODES_DIRECTORY:
602		case B_SYSTEM_NONPACKAGED_BIN_DIRECTORY:
603		case B_SYSTEM_NONPACKAGED_DATA_DIRECTORY:
604		case B_SYSTEM_NONPACKAGED_FONTS_DIRECTORY:
605		case B_SYSTEM_NONPACKAGED_SOUNDS_DIRECTORY:
606		case B_SYSTEM_NONPACKAGED_DOCUMENTATION_DIRECTORY:
607		case B_SYSTEM_NONPACKAGED_LIB_DIRECTORY:
608		case B_SYSTEM_NONPACKAGED_HEADERS_DIRECTORY:
609		case B_SYSTEM_NONPACKAGED_DEVELOP_DIRECTORY:
610			templatePath = kCommonDirectories[which - B_COMMON_DIRECTORY];
611			break;
612
613		/* User directories */
614		case B_USER_DIRECTORY:
615		case B_USER_CONFIG_DIRECTORY:
616		case B_USER_ADDONS_DIRECTORY:
617		case B_USER_BOOT_DIRECTORY:
618		case B_USER_FONTS_DIRECTORY:
619		case B_USER_LIB_DIRECTORY:
620		case B_USER_SETTINGS_DIRECTORY:
621		case B_USER_DESKBAR_DIRECTORY:
622		case B_USER_PRINTERS_DIRECTORY:
623		case B_USER_TRANSLATORS_DIRECTORY:
624		case B_USER_MEDIA_NODES_DIRECTORY:
625		case B_USER_SOUNDS_DIRECTORY:
626		case B_USER_DATA_DIRECTORY:
627		case B_USER_CACHE_DIRECTORY:
628		case B_USER_PACKAGES_DIRECTORY:
629		case B_USER_HEADERS_DIRECTORY:
630		case B_USER_DEVELOP_DIRECTORY:
631		case B_USER_DOCUMENTATION_DIRECTORY:
632		case B_USER_NONPACKAGED_DIRECTORY:
633		case B_USER_NONPACKAGED_ADDONS_DIRECTORY:
634		case B_USER_NONPACKAGED_TRANSLATORS_DIRECTORY:
635		case B_USER_NONPACKAGED_MEDIA_NODES_DIRECTORY:
636		case B_USER_NONPACKAGED_BIN_DIRECTORY:
637		case B_USER_NONPACKAGED_DATA_DIRECTORY:
638		case B_USER_NONPACKAGED_FONTS_DIRECTORY:
639		case B_USER_NONPACKAGED_SOUNDS_DIRECTORY:
640		case B_USER_NONPACKAGED_DOCUMENTATION_DIRECTORY:
641		case B_USER_NONPACKAGED_LIB_DIRECTORY:
642		case B_USER_NONPACKAGED_HEADERS_DIRECTORY:
643		case B_USER_NONPACKAGED_DEVELOP_DIRECTORY:
644		case B_USER_SERVERS_DIRECTORY:
645		case B_USER_APPS_DIRECTORY:
646		case B_USER_BIN_DIRECTORY:
647		case B_USER_PREFERENCES_DIRECTORY:
648		case B_USER_ETC_DIRECTORY:
649		case B_USER_LOG_DIRECTORY:
650		case B_USER_SPOOL_DIRECTORY:
651		case B_USER_VAR_DIRECTORY:
652			templatePath = kUserDirectories[which - B_USER_DIRECTORY];
653			break;
654
655		default:
656			return EINVAL;
657	}
658
659	if (templatePath == NULL)
660		return ENOENT;
661
662	PathBuffer pathBuffer(buffer, pathLength, strlen(buffer));
663
664	// resolve "$h" placeholder to the user's home directory
665	if (!strncmp(templatePath, "$h", 2)) {
666		pathBuffer.Append("/home");
667		templatePath += 2;
668	} else if (templatePath[0] != '\0')
669		pathBuffer.Append('/');
670
671	// resolve "$a" placeholder to the architecture subdirectory, if not
672	// primary
673	if (char* dollar = strchr(templatePath, '$')) {
674		if (dollar[1] == 'a') {
675			pathBuffer.Append(templatePath, dollar - templatePath);
676			templatePath = dollar + 2;
677		}
678	}
679
680	// append (remainder of) template path
681	pathBuffer.Append(templatePath);
682
683	if (pathBuffer.Length() >= pathLength)
684		return E2BIG;
685
686	strlcpy(returnedPath, buffer, pathLength);
687	return B_OK;
688}
689#endif // _LOADER_MODE
690