ossp-pkg/path/path_temp.c
/*
** OSSP path - Filesystem Path Manipulation
** Copyright (c) 2002-2003 Ralf S. Engelschall <rse@engelschall.com>
** Copyright (c) 2002-2003 The OSSP Project <http://www.ossp.org/>
** Copyright (c) 2002-2003 Cable & Wireless Deutschland <http://www.cw.com/de/>
**
** This file is part of OSSP path, a filesystem path manipulation library
** which can be found at http://www.ossp.org/pkg/lib/path/.
**
** Permission to use, copy, modify, and distribute this software for
** any purpose with or without fee is hereby granted, provided that
** the above copyright notice and this permission notice appear in all
** copies.
**
** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
** SUCH DAMAGE.
**
** path_temp.c: temporary pathname creation
*/
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <pwd.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/time.h>
#include "path.h"
#include "path_util.h"
static const char
path_temp_padchar[] =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
path_rc_t
path_temp(
path_temp_t id,
const char *tmpl,
char **res_ptr,
size_t *res_size,
int *res_fd)
{
char tmpl_def[MAXPATHLEN];
char path[MAXPATHLEN];
char *tmpdir;
size_t n;
char *user;
char user_buf[4+((sizeof(long)*8)/3)+10];
const char *cp;
const char *cpT;
char *cpP;
struct passwd *pw;
struct timeval tp;
struct timezone tzp;
pid_t pid;
pid_t ppid;
uid_t uid;
int r;
path_rc_t rc;
int fd;
/* argument sanity checks */
if (res_ptr == NULL && res_fd == NULL)
return PATH_ERR_ARG;
if (id == PATH_TEMP_DIR && res_fd != NULL)
return PATH_ERR_USE;
/* determine temporary directory */
if ((tmpdir = getenv("TMPDIR")) == NULL)
tmpdir = getenv("TEMPDIR");
if (tmpdir == NULL || tmpdir[0] == '\0')
tmpdir = "/tmp";
/* provide a reasonable fallback template */
uid = getuid();
if (tmpl == NULL || tmpl[0] == '\0') {
/* none or empty template */
if ((pw = getpwuid(uid)) != NULL)
user = pw->pw_name;
else {
path_msnprintf(user_buf, sizeof(user_buf), "uid-%ld", (long)uid);
user = user_buf;
}
n = strlen(tmpdir);
path_msnprintf(tmpl_def, sizeof(tmpl_def), "%s%s%s.XXXXXXXX",
tmpdir, (tmpdir[n-1] != '/' ? "/" : ""), user_buf);
tmpl = tmpl_def;
}
else if (tmpl[0] != '/') {
/* non-absolute path template */
n = strlen(tmpdir);
path_msnprintf(tmpl_def, sizeof(tmpl_def), "%s%s%s",
tmpdir, (tmpdir[n-1] != '/' ? "/" : ""), tmpl);
if (strchr(tmpl_def, 'X') == NULL) {
n = strlen(tmpl_def);
path_msnprintf(tmpl_def+n, sizeof(tmpl_def)-n, ".XXXXXXXX");
}
tmpl = tmpl_def;
}
/* seed PRNG as good as possible with POSIX features */
gettimeofday(&tp, &tzp);
pid = getpid();
ppid = getppid();
srand( (unsigned int)tp.tv_sec
+ (unsigned int)tp.tv_usec
+ (unsigned int)uid
+ (unsigned int)pid
+ (unsigned int)ppid);
/* create initial path */
n = 0;
for (cpT = tmpl, cpP = path; *cpT != '\0'; cpT++, cpP++) {
if (*cpT == 'X') {
r = rand() % (sizeof(path_temp_padchar)-1);
*cpP = path_temp_padchar[r];
n++;
}
else
*cpP = *cpT;
}
*cpP = '\0';
if (n < 2)
return PATH_ERR_ARG;
/* try to find a good temporary path */
rc = PATH_ERR_INT;
fd = -1;
for (;;) {
/* try to create path */
if (id == PATH_TEMP_FILE) {
if ((fd = open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0) {
rc = PATH_OK;
break;
}
if (errno != EEXIST)
return PATH_ERR_SYS;
}
else if (id == PATH_TEMP_DIR) {
if (mkdir(path, 0711) == 0) {
rc = PATH_OK;
break;
}
if (errno != EEXIST)
return PATH_ERR_SYS;
}
else
return PATH_ERR_ARG;
/* path collision found, so cycle through namespace */
for (cpT = tmpl, cpP = path; *cpT != '\0'; cpT++, cpP++) {
if (*cpT == 'X') {
cp = strchr(path_temp_padchar, *cpP);
if (cp == NULL || *++cp == '\0')
*cpP = path_temp_padchar[0];
else {
*cpP = *cp;
break;
}
}
}
if (*cpT == '\0')
return PATH_ERR_EXS;
}
/* provide results */
if (rc == PATH_OK) {
if (res_ptr != NULL)
*res_ptr = strdup(path);
else
unlink(path);
if (res_fd != NULL)
*res_fd = fd;
else
close(fd);
}
return rc;
}