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