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 "synonyms.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 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 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 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 * 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 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 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 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 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 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 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 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