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