xref: /haiku/src/system/libroot/os/find_directory.cpp (revision dd2a1e350b303b855a50fd64e6cb55618be1ae6a)
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 	SYSTEM "/data/deskbar/menu",
107 };
108 
109 /* Common directories, shared among users */
110 
111 static 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 
154 static 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 */
197 static int
198 create_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 
233 static size_t
234 get_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 
273 status_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 
515 extern "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 
523 DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__find_directory_alpha4",
524 	"find_directory@", "BASE");
525 
526 DEFINE_LIBROOT_KERNEL_SYMBOL_VERSION("__find_directory", "find_directory@@",
527 	"1_ALPHA5");
528 #else // _LOADER_MODE
529 status_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