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