2 * Copyright (c) 2006, 2007 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 "dirhelper.h"
43 #include "dirhelper_priv.h"
47 #define MUTEX_LOCK(x) if(__is_threaded) pthread_mutex_lock(x)
48 #define MUTEX_UNLOCK(x) if(__is_threaded) pthread_mutex_unlock(x)
51 #define ENCODEDSIZE ((8 * UUID_UID_SIZE + ENCODEBITS - 1) / ENCODEBITS)
52 #define UUID_UID_SIZE (sizeof(uuid_t) + sizeof(uid_t))
54 extern int __is_threaded
;
56 static const mode_t modes
[] = {
62 static const char *subdirs
[] = {
68 static const char encode
[] = "+-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
71 encode_uuid_uid(uuid_t uuid
, uid_t uid
, char *str
)
73 unsigned char buf
[UUID_UID_SIZE
+ 1];
74 unsigned char *bp
= buf
;
78 memcpy(bp
, uuid
, sizeof(uuid_t
));
79 uid
= OSSwapHostToBigInt32(uid
);
80 memcpy(bp
+ sizeof(uuid_t
), &uid
, sizeof(uid_t
));
81 bp
[UUID_UID_SIZE
] = 0; // this ensures the last encoded byte will have trailing zeros
82 while(i
< ENCODEDSIZE
) {
86 *str
++ = encode
[n
>> 2];
89 n
= ((n
& 0x3) << 8) | *bp
++;
90 *str
++ = encode
[n
>> 4];
93 n
= ((n
& 0xf) << 8) | *bp
++;
94 *str
++ = encode
[n
>> 6];
97 *str
++ = encode
[n
& 0x3f];
106 __user_local_dirname(uid_t uid
, dirhelper_which_t which
, char *path
, size_t pathlen
)
108 #if TARGET_OS_EMBEDDED
112 char str
[ENCODEDSIZE
+ 1];
116 if(which
< 0 || which
> DIRHELPER_USER_LOCAL_LAST
) {
121 #if TARGET_OS_EMBEDDED
122 tmpdir
= getenv("TMPDIR");
128 res
= snprintf(path
, pathlen
, "%s/%s", tmpdir
, subdirs
[which
]);
130 res
= mbr_uid_to_uuid(uid
, uuid
);
137 // We partition the namespace so that we don't end up with too
138 // many users in a single directory. With 4096 buckets, we
139 // could scale to 1,000,000 users while keeping the average
140 // number of files in a single directory below 250
142 encode_uuid_uid(uuid
, uid
, str
);
143 res
= snprintf(path
, pathlen
,
145 VAR_FOLDERS_PATH
, BUCKETLEN
, str
, str
, subdirs
[which
]);
149 return NULL
; /* buffer too small */
155 __user_local_mkdir_p(char *path
)
160 next
= path
+ strlen(VAR_FOLDERS_PATH
);
161 while ((next
= strchr(next
, '/')) != NULL
) {
162 *next
= 0; // temporarily truncate
163 res
= mkdir(path
, 0755);
164 if (res
!= 0 && errno
!= EEXIST
)
166 *next
++ = '/'; // restore the slash and increment
171 __private_extern__
char *
172 _dirhelper(dirhelper_which_t which
, char *path
, size_t pathlen
)
174 static char userdir
[PATH_MAX
];
175 static pthread_mutex_t lock
= PTHREAD_MUTEX_INITIALIZER
;
179 if(which
< 0 || which
> DIRHELPER_USER_LOCAL_LAST
) {
188 if(__user_local_dirname(geteuid(), DIRHELPER_USER_LOCAL
, userdir
, sizeof(userdir
)) == NULL
) {
193 * check if userdir exists, and if not, either do the work
194 * ourself if we are root, or call
195 * __dirhelper_create_user_local to create it (we have to
196 * check again afterwards).
198 if(stat(userdir
, &sb
) < 0) {
201 if(errno
!= ENOENT
) { /* some unknown error */
207 * If we are root, lets do what dirhelper does for us.
209 if (geteuid() == 0) {
210 if (__user_local_mkdir_p(userdir
) == NULL
) {
216 if(bootstrap_look_up(bootstrap_port
, DIRHELPER_BOOTSTRAP_NAME
, &mp
) != KERN_SUCCESS
) {
219 mach_port_deallocate(mach_task_self(), mp
);
223 if(__dirhelper_create_user_local(mp
) != KERN_SUCCESS
) {
227 /* double check that the directory really got created */
228 if(stat(userdir
, &sb
) < 0) {
231 mach_port_deallocate(mach_task_self(), mp
);
238 if(pathlen
< strlen(userdir
) + strlen(subdirs
[which
]) + 1) {
240 return NULL
; /* buffer too small */
242 strcpy(path
, userdir
);
243 strcat(path
, subdirs
[which
]);
246 * now for subdirectories, create it with the appropriate permissions
247 * if it doesn't already exist.
249 if(which
!= DIRHELPER_USER_LOCAL
) {
250 res
= mkdir(path
, modes
[which
]);
251 if(res
!= 0 && errno
!= EEXIST
)