]>
Commit | Line | Data |
---|---|---|
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> | |
40 | ||
41 | #include "dirhelper.h" | |
42 | #include "dirhelper_priv.h" | |
43 | ||
44 | #define BUCKETLEN 2 | |
45 | ||
46 | #define MUTEX_LOCK(x) if(__is_threaded) pthread_mutex_lock(x) | |
47 | #define MUTEX_UNLOCK(x) if(__is_threaded) pthread_mutex_unlock(x) | |
48 | ||
49 | #define ENCODEBITS 6 | |
50 | #define ENCODEDSIZE ((8 * UUID_UID_SIZE + ENCODEBITS - 1) / ENCODEBITS) | |
51 | #define UUID_UID_SIZE (sizeof(uuid_t) + sizeof(uid_t)) | |
52 | ||
53 | extern int __is_threaded; | |
54 | ||
55 | static const mode_t modes[] = { | |
56 | 0, /* unused */ | |
57 | 0700, /* temp */ | |
58 | 0700, /* cache */ | |
59 | }; | |
60 | ||
61 | static const char *subdirs[] = { | |
62 | DIRHELPER_TOP_STR, | |
63 | DIRHELPER_TEMP_STR, | |
64 | DIRHELPER_CACHE_STR, | |
65 | }; | |
66 | ||
67 | static const char encode[] = "+-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; | |
68 | ||
69 | static void | |
70 | encode_uuid_uid(uuid_t uuid, uid_t uid, char *str) | |
71 | { | |
72 | unsigned char buf[UUID_UID_SIZE + 1]; | |
73 | unsigned char *bp = buf; | |
74 | int i = 0; | |
75 | unsigned int n; | |
76 | ||
77 | memcpy(bp, uuid, sizeof(uuid_t)); | |
78 | uid = OSSwapHostToBigInt32(uid); | |
79 | memcpy(bp + sizeof(uuid_t), &uid, sizeof(uid_t)); | |
80 | bp[UUID_UID_SIZE] = 0; // this ensures the last encoded byte will have trailing zeros | |
81 | while(i < ENCODEDSIZE) { | |
82 | switch(i % 4) { | |
83 | case 0: | |
84 | n = *bp++; | |
85 | *str++ = encode[n >> 2]; | |
86 | break; | |
87 | case 1: | |
88 | n = ((n & 0x3) << 8) | *bp++; | |
89 | *str++ = encode[n >> 4]; | |
90 | break; | |
91 | case 2: | |
92 | n = ((n & 0xf) << 8) | *bp++; | |
93 | *str++ = encode[n >> 6]; | |
94 | break; | |
95 | case 3: | |
96 | *str++ = encode[n & 0x3f]; | |
97 | break; | |
98 | } | |
99 | i++; | |
100 | } | |
101 | *str = 0; | |
102 | } | |
103 | ||
104 | char * | |
105 | __user_local_dirname(uid_t uid, dirhelper_which_t which, char *path, size_t pathlen) | |
106 | { | |
107 | uuid_t uuid; | |
108 | char str[ENCODEDSIZE + 1]; | |
109 | int res; | |
110 | ||
111 | if(which < 0 || which > DIRHELPER_USER_LOCAL_LAST) { | |
112 | errno = EINVAL; | |
113 | return NULL; | |
114 | } | |
115 | ||
116 | res = mbr_uid_to_uuid(uid, uuid); | |
117 | if(res != 0) { | |
118 | errno = res; | |
119 | return NULL; | |
120 | } | |
121 | ||
122 | // | |
123 | // We partition the namespace so that we don't end up with too | |
124 | // many users in a single directory. With 4096 buckets, we | |
125 | // could scale to 1,000,000 users while keeping the average | |
126 | // number of files in a single directory below 250 | |
127 | // | |
128 | encode_uuid_uid(uuid, uid, str); | |
129 | res = snprintf(path, pathlen, | |
130 | "%s%.*s/%s/%s", | |
131 | VAR_FOLDERS_PATH, BUCKETLEN, str, str, subdirs[which]); | |
132 | if(res >= pathlen) { | |
133 | errno = EINVAL; | |
134 | return NULL; /* buffer too small */ | |
135 | } | |
136 | return path; | |
137 | } | |
138 | ||
139 | __private_extern__ char * | |
140 | _dirhelper(dirhelper_which_t which, char *path, size_t pathlen) | |
141 | { | |
142 | static char userdir[PATH_MAX]; | |
143 | static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; | |
144 | int res; | |
145 | struct stat sb; | |
146 | ||
147 | if(which < 0 || which > DIRHELPER_USER_LOCAL_LAST) { | |
148 | errno = EINVAL; | |
149 | return NULL; | |
150 | } | |
151 | ||
152 | if(!*userdir) { | |
153 | MUTEX_LOCK(&lock); | |
154 | if(!*userdir) { | |
155 | mach_port_t mp; | |
156 | ||
157 | if(bootstrap_look_up(bootstrap_port, DIRHELPER_BOOTSTRAP_NAME, &mp) != KERN_SUCCESS) { | |
158 | errno = EPERM; | |
159 | MUTEX_UNLOCK(&lock); | |
160 | return NULL; | |
161 | } | |
162 | if(__user_local_dirname(geteuid(), DIRHELPER_USER_LOCAL, userdir, sizeof(userdir)) == NULL) { | |
163 | server_error: | |
164 | mach_port_deallocate(mach_task_self(), mp); | |
165 | MUTEX_UNLOCK(&lock); | |
166 | return NULL; | |
167 | } | |
168 | /* | |
169 | * check if userdir exists, and if not, call | |
170 | * __dirhelper_create_user_local to create it | |
171 | * (we have to check again afterwards). | |
172 | */ | |
173 | if(stat(userdir, &sb) < 0) { | |
174 | if(errno != ENOENT) { /* some unknown error */ | |
175 | *userdir = 0; | |
176 | goto server_error; | |
177 | } | |
178 | if(__dirhelper_create_user_local(mp) != KERN_SUCCESS) { | |
179 | errno = EPERM; | |
180 | *userdir = 0; | |
181 | goto server_error; | |
182 | } | |
183 | /* double check that the directory really got created */ | |
184 | if(stat(userdir, &sb) < 0) { | |
185 | *userdir = 0; | |
186 | goto server_error; | |
187 | } | |
188 | } | |
189 | mach_port_deallocate(mach_task_self(), mp); | |
190 | } | |
191 | MUTEX_UNLOCK(&lock); | |
192 | } | |
193 | ||
194 | if(pathlen < strlen(userdir) + strlen(subdirs[which]) + 1) { | |
195 | errno = EINVAL; | |
196 | return NULL; /* buffer too small */ | |
197 | } | |
198 | strcpy(path, userdir); | |
199 | strcat(path, subdirs[which]); | |
200 | ||
201 | /* | |
202 | * now for subdirectories, create it with the appropriate permissions | |
203 | * if it doesn't already exist. | |
204 | */ | |
205 | if(which != DIRHELPER_USER_LOCAL) { | |
206 | res = mkdir(path, modes[which]); | |
207 | if(res != 0 && errno != EEXIST) | |
208 | return NULL; | |
209 | } | |
210 | ||
211 | return path; | |
212 | } |