]> git.saurik.com Git - apple/libc.git/blame - darwin/_dirhelper.c
Libc-498.1.7.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>
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
53extern int __is_threaded;
54
55static const mode_t modes[] = {
56 0, /* unused */
57 0700, /* temp */
58 0700, /* cache */
59};
60
61static const char *subdirs[] = {
62 DIRHELPER_TOP_STR,
63 DIRHELPER_TEMP_STR,
64 DIRHELPER_CACHE_STR,
65};
66
67static const char encode[] = "+-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
68
69static void
70encode_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
104char *
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) {
163server_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}