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