1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 #include "lint.h"
30 #include "mtlib.h"
31 #include <sys/types.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <sys/stat.h>
35 #include <unistd.h>
36 #include <stdlib.h>
37 #include <limits.h>
38 #include <pthread.h>
39 #include <thread.h>
40 #include <string.h>
41 #include <dirent.h>
42 #include <stdio.h>
43 #include <dlfcn.h>
44 #include <atomic.h>
45 #include <md5.h>
46 #include "pos4obj.h"
47
48 #define HASHSTRLEN 32
49
50 static char *__pos4obj_name(const char *, const char *);
51 static void __pos4obj_md5toa(unsigned char *, unsigned char *);
52 static void __pos4obj_clean(char *);
53
54 static char objroot[] = "/tmp/";
55 static long int name_max = 0;
56
57 int
__open_nc(const char * path,int oflag,mode_t mode)58 __open_nc(const char *path, int oflag, mode_t mode)
59 {
60 int cancel_state;
61 int val;
62 struct stat64 statbuf;
63
64 /*
65 * Ensure path is not a symlink to somewhere else. This provides
66 * a modest amount of protection against easy security attacks.
67 */
68 if (lstat64(path, &statbuf) == 0) {
69 if (S_ISLNK(statbuf.st_mode)) {
70 errno = EINVAL;
71 return (-1);
72 }
73 }
74
75 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
76 val = open64(path, oflag, mode);
77 (void) pthread_setcancelstate(cancel_state, NULL);
78
79 return (val);
80 }
81
82 int
__close_nc(int fildes)83 __close_nc(int fildes)
84 {
85 int cancel_state;
86 int val;
87
88 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
89 val = close(fildes);
90 (void) pthread_setcancelstate(cancel_state, NULL);
91
92 return (val);
93 }
94
95 /*
96 * This is to avoid loading libmd.so.1 unless we absolutely have to.
97 */
98 typedef void (*md5_calc_t)(unsigned char *, unsigned char *, unsigned int);
99 static md5_calc_t real_md5_calc = NULL;
100 static mutex_t md5_lock = DEFAULTMUTEX;
101
102 static void
load_md5_calc(void)103 load_md5_calc(void)
104 {
105 void *md5_handle = dlopen("libmd.so.1", RTLD_LAZY);
106 md5_calc_t md5_calc = (md5_handle == NULL)? NULL :
107 (md5_calc_t)dlsym(md5_handle, "md5_calc");
108
109 lmutex_lock(&md5_lock);
110 if (real_md5_calc == NULL) {
111 if (md5_calc == NULL)
112 real_md5_calc = (md5_calc_t)(-1);
113 else {
114 real_md5_calc = md5_calc;
115 md5_handle = NULL; /* don't dlclose it */
116 }
117 membar_producer();
118 }
119 lmutex_unlock(&md5_lock);
120
121 if (md5_handle)
122 (void) dlclose(md5_handle);
123 }
124
125 static char *
__pos4obj_name(const char * path,const char * type)126 __pos4obj_name(const char *path, const char *type)
127 {
128 int shortpath = 1;
129 int olderrno;
130 size_t len;
131 char *dfile;
132 unsigned char hashbuf[HASHSTRLEN + 1];
133 unsigned char md5_digest[MD5_DIGEST_LENGTH];
134
135 /*
136 * If the path is path_max - strlen(type) characters or less,
137 * the name of the file to use will be the path prefixed by
138 * the type.
139 *
140 * In the special case where the path is longer than
141 * path_max - strlen(type) characters, we create a string based on the
142 * MD5 hash of the path. We prefix that string with a '.' to
143 * make it obscure, and create a directory in objroot with
144 * that name. In that directory, we create a directory named
145 * after the type of object requested. Inside the type
146 * directory, the filename will be the path of the object. This
147 * prevents collisions in all namespaces.
148 *
149 * Example:
150 * Let objroot = "/tmp/", path = "/<longpath>", and type = ".MQD"
151 * Let the MD5 hash of "<longpath>" = "<hash>"
152 *
153 * The desired file is /tmp/.<hash>/.MQD/<longpath>
154 */
155
156 /*
157 * Do not include the leading '/' in the path length.
158 * Assumes __pos4obj_check(path) has already been called.
159 */
160 if ((strlen(path) - 1) > (name_max - strlen(type)))
161 shortpath = 0;
162
163 if (shortpath) {
164 /*
165 * strlen(path) includes leading slash as space for NUL.
166 */
167 len = strlen(objroot) + strlen(type) + strlen(path);
168 } else {
169 /*
170 * Long path name. Add 3 for extra '/', '.' and '\0'
171 */
172 len = strlen(objroot) + HASHSTRLEN + strlen(type) +
173 strlen(path) + 3;
174 }
175
176 if ((dfile = malloc(len)) == NULL)
177 return (NULL);
178
179 (void) memset(dfile, 0, len);
180 (void) strcpy(dfile, objroot);
181
182 if (shortpath) {
183 (void) strcat(dfile, type);
184 (void) strcat(dfile, path + 1);
185 return (dfile);
186 }
187
188 /*
189 * If we can successfully load it, call md5_calc().
190 * Otherwise, (this "can't happen") return NULL.
191 */
192 if (real_md5_calc == NULL)
193 load_md5_calc();
194 if (real_md5_calc == (md5_calc_t)(-1)) {
195 free(dfile);
196 return (NULL);
197 }
198
199 real_md5_calc(md5_digest, (unsigned char *)path + 1, strlen(path + 1));
200 __pos4obj_md5toa(hashbuf, md5_digest);
201 (void) strcat(dfile, ".");
202 (void) strcat(dfile, (const char *)hashbuf);
203
204 /*
205 * Errno must be preserved across the following calls to
206 * mkdir. This needs to be done to prevent incorrect error
207 * reporting in certain cases. When we attempt to open a
208 * non-existent object without the O_CREAT flag, it will
209 * always create a lock file first. The lock file is created
210 * and then the open is attempted, but fails with ENOENT. The
211 * lock file is then destroyed. In the following code path, we
212 * are finding the absolute path to the lock file after
213 * already having attempted the open (which set errno to
214 * ENOENT). The following calls to mkdir will return -1 and
215 * set errno to EEXIST, since the hash and type directories
216 * were created when the lock file was created. The correct
217 * errno is the ENOENT from the attempted open of the desired
218 * object.
219 */
220 olderrno = errno;
221
222 /*
223 * Create hash directory. Use 777 permissions so everyone can use it.
224 */
225 if (mkdir(dfile, S_IRWXU|S_IRWXG|S_IRWXO) == 0) {
226 if (chmod(dfile, S_IRWXU|S_IRWXG|S_IRWXO) == -1) {
227 free(dfile);
228 return (NULL);
229 }
230 } else {
231 if (errno != EEXIST) {
232 free(dfile);
233 return (NULL);
234 }
235 }
236
237 (void) strcat(dfile, "/");
238 (void) strcat(dfile, type);
239
240 /*
241 * Create directory for requested type. Use 777 perms so everyone
242 * can use it.
243 */
244 if (mkdir(dfile, S_IRWXU|S_IRWXG|S_IRWXO) == 0) {
245 if (chmod(dfile, S_IRWXU|S_IRWXG|S_IRWXO) == -1) {
246 free(dfile);
247 return (NULL);
248 }
249 } else {
250 if (errno != EEXIST) {
251 free(dfile);
252 return (NULL);
253 }
254 }
255
256 errno = olderrno;
257 (void) strcat(dfile, path);
258 return (dfile);
259 }
260
261 /*
262 * Takes a 128-bit MD5 digest and transforms to a sequence of 32 ASCII
263 * characters. Output is the hexadecimal representation of the digest.
264 *
265 * The output buffer must be at least HASHSTRLEN + 1 characters
266 * long. HASHSTRLEN is the size of the MD5 digest (128 bits)
267 * divided by the number of bits used per char of output (4). The
268 * extra character at the end is for the NUL terminating character.
269 */
270
271 static void
__pos4obj_md5toa(unsigned char * dest,unsigned char * src)272 __pos4obj_md5toa(unsigned char *dest, unsigned char *src)
273 {
274 int i;
275 uint32_t *p;
276
277 /* LINTED pointer cast may result in improper alignment */
278 p = (uint32_t *)src;
279
280 for (i = 0; i < (MD5_DIGEST_LENGTH / 4); i++)
281 (void) snprintf((char *)dest + (i * 8), 9, "%.8x", *p++);
282
283 dest[HASHSTRLEN] = '\0';
284 }
285
286 /*
287 * This open function assume that there is no simultaneous
288 * open/unlink operation is going on. The caller is supposed
289 * to ensure that both open in O_CREAT mode happen atomically.
290 * It returns the crflag as 1 if file is created else 0.
291 */
292 int
__pos4obj_open(const char * name,char * type,int oflag,mode_t mode,int * crflag)293 __pos4obj_open(const char *name, char *type, int oflag,
294 mode_t mode, int *crflag)
295 {
296 int fd;
297 char *dfile;
298
299 errno = 0;
300 *crflag = 0;
301
302 if ((dfile = __pos4obj_name(name, type)) == NULL) {
303 return (-1);
304 }
305
306 if (!(oflag & O_CREAT)) {
307 if ((fd = __open_nc(dfile, oflag, mode)) == -1)
308 __pos4obj_clean(dfile);
309
310 free(dfile);
311 return (fd);
312 }
313
314 /*
315 * We need to make sure that crflag is set iff we actually create
316 * the file. We do this by or'ing in O_EXCL, and attempting an
317 * open. If that fails with an EEXIST, and O_EXCL wasn't specified
318 * by the caller, then the file seems to exist; we'll try an
319 * open with O_CREAT cleared. If that succeeds, then the file
320 * did indeed exist. If that fails with an ENOENT, however, the
321 * file was removed between the opens; we need to take another
322 * lap.
323 */
324 for (;;) {
325 if ((fd = __open_nc(dfile, (oflag | O_EXCL), mode)) == -1) {
326 if (errno == EEXIST && !(oflag & O_EXCL)) {
327 fd = __open_nc(dfile, oflag & ~O_CREAT, mode);
328
329 if (fd == -1 && errno == ENOENT)
330 continue;
331 break;
332 }
333 } else {
334 *crflag = 1;
335 }
336 break;
337 }
338
339 free(dfile);
340 return (fd);
341 }
342
343
344 int
__pos4obj_unlink(const char * name,const char * type)345 __pos4obj_unlink(const char *name, const char *type)
346 {
347 int err;
348 char *dfile;
349
350 if ((dfile = __pos4obj_name(name, type)) == NULL) {
351 return (-1);
352 }
353
354 err = unlink(dfile);
355
356 __pos4obj_clean(dfile);
357
358 free(dfile);
359
360 return (err);
361 }
362
363 /*
364 * This function opens the lock file for each named object
365 * the presence of this file in the file system is the lock
366 */
367 int
__pos4obj_lock(const char * name,const char * ltype)368 __pos4obj_lock(const char *name, const char *ltype)
369 {
370 char *dfile;
371 int fd;
372 int limit = 64;
373
374 if ((dfile = __pos4obj_name(name, ltype)) == NULL) {
375 return (-1);
376 }
377
378 while (limit-- > 0) {
379 if ((fd = __open_nc(dfile, O_RDWR | O_CREAT | O_EXCL, 0666))
380 < 0) {
381 if (errno != EEXIST)
382 break;
383 (void) sleep(1);
384 continue;
385 }
386
387 (void) __close_nc(fd);
388 free(dfile);
389 return (1);
390 }
391
392 free(dfile);
393 return (-1);
394 }
395
396 /*
397 * Unlocks the file by unlinking it from the filesystem
398 */
399 int
__pos4obj_unlock(const char * path,const char * type)400 __pos4obj_unlock(const char *path, const char *type)
401 {
402 return (__pos4obj_unlink(path, type));
403 }
404
405 /*
406 * Removes unused hash and type directories that may exist in specified path.
407 */
408 static void
__pos4obj_clean(char * path)409 __pos4obj_clean(char *path)
410 {
411 char *p;
412 int olderrno;
413
414 /*
415 * path is either
416 * 1) /<objroot>/<type><path> or
417 * 2) /<objroot>/.<hash>/<type>/<path>
418 *
419 * In case 1, there is nothing to clean.
420 *
421 * Detect case 2 by looking for a '/' after /objroot/ and
422 * remove the two trailing directories, if empty.
423 */
424 if (strchr(path + strlen(objroot), '/') == NULL)
425 return;
426
427 /*
428 * Preserve errno across calls to rmdir. See block comment in
429 * __pos4obj_name() for explanation.
430 */
431 olderrno = errno;
432
433 if ((p = strrchr(path, '/')) == NULL)
434 return;
435 *p = '\0';
436
437 (void) rmdir(path);
438
439 if ((p = strrchr(path, '/')) == NULL)
440 return;
441 *p = '\0';
442
443 (void) rmdir(path);
444
445 errno = olderrno;
446 }
447
448
449 /*
450 * Check that path starts with a /, does not contain a / within it
451 * and is not longer than PATH_MAX or NAME_MAX
452 */
453 int
__pos4obj_check(const char * path)454 __pos4obj_check(const char *path)
455 {
456 long int i;
457
458 /*
459 * This assumes that __pos4obj_check() is called before
460 * any of the other functions in this file
461 */
462 if (name_max == 0 || name_max == -1) {
463 name_max = pathconf(objroot, _PC_NAME_MAX);
464 if (name_max == -1)
465 return (-1);
466 }
467
468 if (*path++ != '/') {
469 errno = EINVAL;
470 return (-1);
471 }
472
473 for (i = 0; *path != '\0'; i++) {
474 if (*path++ == '/') {
475 errno = EINVAL;
476 return (-1);
477 }
478 }
479
480 if (i > PATH_MAX || i > name_max) {
481 errno = ENAMETOOLONG;
482 return (-1);
483 }
484
485 return (0);
486 }
487