Home | History | Annotate | Download | only in rt
      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 "lint.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