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