]> git.saurik.com Git - apple/libc.git/blame - darwin/_dirhelper.c
Libc-594.9.5.tar.gz
[apple/libc.git] / darwin / _dirhelper.c
CommitLineData
224c7076
A
1/*
2 * Copyright (c) 2006, 2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <mach/mach.h>
25#include <mach/mach_error.h>
26#include <servers/bootstrap.h>
27#include <unistd.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <stdbool.h>
31#include <membership.h>
32#include <pthread.h>
33#include <errno.h>
34#include <sys/types.h>
35#include <sys/stat.h>
36#include <sys/param.h>
37#include <uuid/uuid.h>
38#include <string.h>
39#include <libkern/OSByteOrder.h>
511daa4c 40#include <TargetConditionals.h>
224c7076
A
41
42#include "dirhelper.h"
43#include "dirhelper_priv.h"
44
45#define BUCKETLEN 2
46
47#define MUTEX_LOCK(x) if(__is_threaded) pthread_mutex_lock(x)
48#define MUTEX_UNLOCK(x) if(__is_threaded) pthread_mutex_unlock(x)
49
50#define ENCODEBITS 6
51#define ENCODEDSIZE ((8 * UUID_UID_SIZE + ENCODEBITS - 1) / ENCODEBITS)
52#define UUID_UID_SIZE (sizeof(uuid_t) + sizeof(uid_t))
53
54extern int __is_threaded;
55
56static const mode_t modes[] = {
57 0, /* unused */
58 0700, /* temp */
59 0700, /* cache */
60};
61
62static const char *subdirs[] = {
63 DIRHELPER_TOP_STR,
64 DIRHELPER_TEMP_STR,
65 DIRHELPER_CACHE_STR,
66};
67
68static const char encode[] = "+-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
69
70static void
71encode_uuid_uid(uuid_t uuid, uid_t uid, char *str)
72{
73 unsigned char buf[UUID_UID_SIZE + 1];
74 unsigned char *bp = buf;
75 int i = 0;
76 unsigned int n;
77
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) {
83 switch(i % 4) {
84 case 0:
85 n = *bp++;
86 *str++ = encode[n >> 2];
87 break;
88 case 1:
89 n = ((n & 0x3) << 8) | *bp++;
90 *str++ = encode[n >> 4];
91 break;
92 case 2:
93 n = ((n & 0xf) << 8) | *bp++;
94 *str++ = encode[n >> 6];
95 break;
96 case 3:
97 *str++ = encode[n & 0x3f];
98 break;
99 }
100 i++;
101 }
102 *str = 0;
103}
104
105char *
106__user_local_dirname(uid_t uid, dirhelper_which_t which, char *path, size_t pathlen)
107{
511daa4c
A
108#if TARGET_OS_EMBEDDED
109 char *tmpdir;
110#else
224c7076
A
111 uuid_t uuid;
112 char str[ENCODEDSIZE + 1];
511daa4c 113#endif
224c7076
A
114 int res;
115
116 if(which < 0 || which > DIRHELPER_USER_LOCAL_LAST) {
117 errno = EINVAL;
118 return NULL;
119 }
120
511daa4c
A
121#if TARGET_OS_EMBEDDED
122 tmpdir = getenv("TMPDIR");
123 if(!tmpdir) {
124 errno = EINVAL;
125 return NULL;
126 }
127
128 res = snprintf(path, pathlen, "%s/%s", tmpdir, subdirs[which]);
129#else
224c7076
A
130 res = mbr_uid_to_uuid(uid, uuid);
131 if(res != 0) {
132 errno = res;
133 return NULL;
134 }
135
136 //
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
141 //
142 encode_uuid_uid(uuid, uid, str);
143 res = snprintf(path, pathlen,
144 "%s%.*s/%s/%s",
145 VAR_FOLDERS_PATH, BUCKETLEN, str, str, subdirs[which]);
511daa4c 146#endif
224c7076
A
147 if(res >= pathlen) {
148 errno = EINVAL;
149 return NULL; /* buffer too small */
150 }
151 return path;
152}
153
34e8f829
A
154char *
155__user_local_mkdir_p(char *path)
156{
157 char *next;
158 int res;
159
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)
165 return NULL;
166 *next++ = '/'; // restore the slash and increment
167 }
168 return path;
169}
170
224c7076
A
171__private_extern__ char *
172_dirhelper(dirhelper_which_t which, char *path, size_t pathlen)
173{
174 static char userdir[PATH_MAX];
175 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
176 int res;
177 struct stat sb;
178
179 if(which < 0 || which > DIRHELPER_USER_LOCAL_LAST) {
180 errno = EINVAL;
181 return NULL;
182 }
183
184 if(!*userdir) {
185 MUTEX_LOCK(&lock);
34e8f829
A
186 if (!*userdir) {
187
224c7076 188 if(__user_local_dirname(geteuid(), DIRHELPER_USER_LOCAL, userdir, sizeof(userdir)) == NULL) {
224c7076
A
189 MUTEX_UNLOCK(&lock);
190 return NULL;
191 }
192 /*
34e8f829
A
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).
224c7076
A
197 */
198 if(stat(userdir, &sb) < 0) {
34e8f829
A
199 mach_port_t mp;
200
224c7076
A
201 if(errno != ENOENT) { /* some unknown error */
202 *userdir = 0;
34e8f829
A
203 MUTEX_UNLOCK(&lock);
204 return NULL;
224c7076 205 }
34e8f829
A
206 /*
207 * If we are root, lets do what dirhelper does for us.
208 */
209 if (geteuid() == 0) {
210 if (__user_local_mkdir_p(userdir) == NULL) {
211 *userdir = 0;
212 MUTEX_UNLOCK(&lock);
213 return NULL;
214 }
215 } else {
216 if(bootstrap_look_up(bootstrap_port, DIRHELPER_BOOTSTRAP_NAME, &mp) != KERN_SUCCESS) {
217 errno = EPERM;
218 server_error:
219 mach_port_deallocate(mach_task_self(), mp);
220 MUTEX_UNLOCK(&lock);
221 return NULL;
222 }
223 if(__dirhelper_create_user_local(mp) != KERN_SUCCESS) {
224 errno = EPERM;
225 goto server_error;
226 }
227 /* double check that the directory really got created */
228 if(stat(userdir, &sb) < 0) {
229 goto server_error;
230 }
231 mach_port_deallocate(mach_task_self(), mp);
224c7076
A
232 }
233 }
224c7076
A
234 }
235 MUTEX_UNLOCK(&lock);
236 }
237
238 if(pathlen < strlen(userdir) + strlen(subdirs[which]) + 1) {
239 errno = EINVAL;
240 return NULL; /* buffer too small */
241 }
242 strcpy(path, userdir);
243 strcat(path, subdirs[which]);
244
245 /*
246 * now for subdirectories, create it with the appropriate permissions
247 * if it doesn't already exist.
248 */
249 if(which != DIRHELPER_USER_LOCAL) {
250 res = mkdir(path, modes[which]);
251 if(res != 0 && errno != EEXIST)
252 return NULL;
253 }
254
255 return path;
256}