2 * Copyright (c) 2006, 2007, 2010 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 #include <mach/mach.h>
25 #include <mach/mach_error.h>
26 #include <servers/bootstrap.h>
31 #include <membership.h>
34 #include <sys/types.h>
36 #include <sys/param.h>
37 #include <uuid/uuid.h>
39 #include <libkern/OSByteOrder.h>
40 #include <TargetConditionals.h>
42 #include <xpc/private.h>
44 #include "dirhelper.h"
45 #include "dirhelper_priv.h"
49 #define MUTEX_LOCK(x) if(__is_threaded) pthread_mutex_lock(x)
50 #define MUTEX_UNLOCK(x) if(__is_threaded) pthread_mutex_unlock(x)
52 // Use 5 bits per character, to avoid uppercase and shell magic characters
54 #define ENCODEDSIZE ((8 * UUID_UID_SIZE + ENCODEBITS - 1) / ENCODEBITS)
55 #define MASK(x) ((1 << (x)) - 1)
56 #define UUID_UID_SIZE (sizeof(uuid_t) + sizeof(uid_t))
58 extern int __is_threaded
;
60 static const mode_t modes
[] = {
66 static const char *subdirs
[] = {
72 static pthread_once_t userdir_control
= PTHREAD_ONCE_INIT
;
73 static char *userdir
= NULL
;
75 // lower case letter (minus vowels), plus numbers and _, making
77 static const char encode
[] = "0123456789_bcdfghjklmnpqrstvwxyz";
80 encode_uuid_uid(const uuid_t uuid
, uid_t uid
, char *str
)
82 unsigned char buf
[UUID_UID_SIZE
+ 1];
83 unsigned char *bp
= buf
;
87 memcpy(bp
, uuid
, sizeof(uuid_t
));
88 uid
= OSSwapHostToBigInt32(uid
);
89 memcpy(bp
+ sizeof(uuid_t
), &uid
, sizeof(uid_t
));
90 bp
[UUID_UID_SIZE
] = 0; // this ensures the last encoded byte will have trailing zeros
91 for(i
= 0; i
< ENCODEDSIZE
; i
++) {
92 // 5 bits has 8 states
96 *str
++ = encode
[n
>> 3];
99 n
= ((n
& MASK(3)) << 8) | *bp
++;
100 *str
++ = encode
[n
>> 6];
104 *str
++ = encode
[n
>> 1];
107 n
= ((n
& MASK(1)) << 8) | *bp
++;
108 *str
++ = encode
[n
>> 4];
111 n
= ((n
& MASK(4)) << 8) | *bp
++;
112 *str
++ = encode
[n
>> 7];
116 *str
++ = encode
[n
>> 2];
119 n
= ((n
& MASK(2)) << 8) | *bp
++;
120 *str
++ = encode
[n
>> 5];
123 *str
++ = encode
[n
& MASK(5)];
131 __user_local_dirname(uid_t uid
, dirhelper_which_t which
, char *path
, size_t pathlen
)
137 char str
[ENCODEDSIZE
+ 1];
141 if(which
< 0 || which
> DIRHELPER_USER_LOCAL_LAST
) {
147 /* We only support DIRHELPER_USER_LOCAL_TEMP on embedded.
148 * This interface really doesn't map from OSX to embedded,
149 * and clients of this interface will need to adapt when
150 * porting their applications to embedded.
151 * See: <rdar://problem/7515613>
153 if(which
== DIRHELPER_USER_LOCAL_TEMP
) {
154 tmpdir
= getenv("TMPDIR");
159 res
= snprintf(path
, pathlen
, "%s", tmpdir
);
165 res
= mbr_uid_to_uuid(uid
, uuid
);
172 // We partition the namespace so that we don't end up with too
173 // many users in a single directory. With 1024 buckets, we
174 // could scale to 1,000,000 users while keeping the average
175 // number of files in a single directory around 1000
177 encode_uuid_uid(uuid
, uid
, str
);
178 res
= snprintf(path
, pathlen
,
180 VAR_FOLDERS_PATH
, BUCKETLEN
, str
, str
+ BUCKETLEN
, subdirs
[which
]);
184 return NULL
; /* buffer too small */
190 __user_local_mkdir_p(char *path
)
195 next
= path
+ strlen(VAR_FOLDERS_PATH
);
196 while ((next
= strchr(next
, '/')) != NULL
) {
197 *next
= 0; // temporarily truncate
198 res
= mkdir(path
, 0755);
199 if (res
!= 0 && errno
!= EEXIST
)
201 *next
++ = '/'; // restore the slash and increment
206 static void userdir_allocate(void)
208 userdir
= calloc(PATH_MAX
, sizeof(char));
212 * 9407258: Invalidate the dirhelper cache (userdir) of the child after fork.
213 * There is a rare case when launchd will have userdir set, and child process
214 * will sometimes inherit this cached value.
216 __private_extern__
void
217 _dirhelper_fork_child(void)
219 if(userdir
) *userdir
= 0;
222 __private_extern__
char *
223 _dirhelper(dirhelper_which_t which
, char *path
, size_t pathlen
)
225 static pthread_mutex_t lock
= PTHREAD_MUTEX_INITIALIZER
;
228 if(which
< 0 || which
> DIRHELPER_USER_LOCAL_LAST
) {
233 if (pthread_once(&userdir_control
, userdir_allocate
)
243 if(__user_local_dirname(geteuid(), DIRHELPER_USER_LOCAL
, userdir
, PATH_MAX
) == NULL
) {
248 * All dirhelper directories are now at the same level, so
249 * we need to remove the DIRHELPER_TOP_STR suffix to get the
252 userdir
[strlen(userdir
) - (sizeof(DIRHELPER_TOP_STR
) - 1)] = 0;
254 * check if userdir exists, and if not, either do the work
255 * ourself if we are root, or call
256 * __dirhelper_create_user_local to create it (we have to
257 * check again afterwards).
259 if(stat(userdir
, &sb
) < 0) {
262 if(errno
!= ENOENT
) { /* some unknown error */
268 * If we are root, lets do what dirhelper does for us.
270 if (geteuid() == 0) {
271 if (__user_local_mkdir_p(userdir
) == NULL
) {
277 if(bootstrap_look_up(bootstrap_port
, DIRHELPER_BOOTSTRAP_NAME
, &mp
) != KERN_SUCCESS
) {
280 mach_port_deallocate(mach_task_self(), mp
);
284 if(__dirhelper_create_user_local(mp
) != KERN_SUCCESS
) {
288 /* double check that the directory really got created */
289 if(stat(userdir
, &sb
) < 0) {
292 mach_port_deallocate(mach_task_self(), mp
);
299 if(pathlen
< strlen(userdir
) + strlen(subdirs
[which
]) + 1) {
301 return NULL
; /* buffer too small */
303 strcpy(path
, userdir
);
304 strcat(path
, subdirs
[which
]);
307 * create the subdir with the appropriate permissions if it doesn't already
308 * exist. On OS X, if we're under App Sandbox, we rely on the subdir having
309 * been already created for us.
311 #if !TARGET_OS_IPHONE
312 if (!_xpc_runtime_is_app_sandboxed())
314 if(mkdir(path
, modes
[which
]) != 0 && errno
!= EEXIST
)
317 #if !TARGET_OS_IPHONE
318 char *userdir_suffix
= NULL
;
320 if (_xpc_runtime_is_app_sandboxed()) {
322 * if the subdir wasn't made for us, bail since we probably don't have
323 * permission to create it ourselves.
325 if(stat(path
, &sb
) < 0) {
331 * sandboxed applications get per-application directories named
332 * after the container
334 userdir_suffix
= getenv(XPC_ENV_SANDBOX_CONTAINER_ID
);
335 if (!userdir_suffix
) {
340 userdir_suffix
= getenv(DIRHELPER_ENV_USER_DIR_SUFFIX
);
342 if (userdir_suffix
) {
344 * suffix (usually container ID) doesn't end in a slash, so +2 is for slash and \0
346 if (pathlen
< strlen(path
) + strlen(userdir_suffix
) + 2) {
348 return NULL
; /* buffer too small */
351 strcat(path
, userdir_suffix
);
355 * create suffix subdirectory with the appropriate permissions
356 * if it doesn't already exist.
358 if (mkdir(path
, modes
[which
]) != 0 && errno
!= EEXIST
)
362 * update TMPDIR if necessary
364 if (which
== DIRHELPER_USER_LOCAL_TEMP
) {
365 char *tmpdir
= getenv("TMPDIR");
366 if (!tmpdir
|| strncmp(tmpdir
, path
, strlen(path
)))
367 setenv("TMPDIR", path
, 1);