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