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