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