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