/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 1989, 2012, Oracle and/or its affiliates. All rights reserved.
*/
/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */
#include "lint.h"
#include <sys/fcntl.h>
#include <sys/param.h>
#include <sys/syscall.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
/*
* Call the kernel implementation of *realpath. It either takes
* a path (with fd = AT_FDCWD) or a file descriptor.
* If no buffer is passed in, we allocate a properly sized buffer.
* If the buffer size is 0, we will grow it until the kernel will
* returns a value or an error other than ERANGE.
*/
static char *
krealpath_impl(int fd, const char *path, char *buf, size_t bufsize)
{
int ret;
char *kbuf;
size_t mysize = bufsize;
char sbuf[MAXPATHLEN];
if (buf == NULL) {
if (bufsize <= sizeof (sbuf)) {
kbuf = sbuf;
if (bufsize == 0)
mysize = sizeof (sbuf);
} else {
kbuf = malloc(bufsize);
if (kbuf == NULL)
return (NULL);
}
} else {
kbuf = buf;
}
do {
ret = syscall(SYS_frealpathat, fd, path, kbuf, mysize);
if (ret == 0 || buf != NULL || bufsize != 0 || errno != ERANGE)
break;
mysize *= 2;
if (buf != sbuf)
free(kbuf);
kbuf = malloc(mysize);
} while (kbuf != NULL);
if (ret != 0 || kbuf == NULL) {
if (buf != kbuf && kbuf != sbuf)
free(kbuf);
/* Standard requires ENAMETOOLONG and EINVAL. */
if (errno == ERANGE)
errno = ENAMETOOLONG;
else if (errno == EFAULT)
errno = EINVAL;
return (NULL);
}
if (buf == NULL) {
buf = strdup(kbuf);
if (kbuf != sbuf)
free(kbuf);
}
return (buf);
}
/*
* Canonicalize the path given in file_name, resolving away all symbolic link
* components. Store the result into the buffer named by resolved_name, which
* must be long enough. If no buffer is given, there is no limit. Returns NULL
* on failure and resolved_name on success. On failure, to maintain
* compatibility with the past, the contents of file_name will be copied
* into resolved_name.
*/
char *
realpath(const char *file_name, char *resolved_name)
{
char *ret;
ret = krealpath_impl(AT_FDCWD, file_name, resolved_name,
resolved_name == NULL ? 0 : MAXPATHLEN);
/*
* EINVAL was likely mapped from EFAULT;
* we cannot dereference file_name or resolved_name.
*/
if (ret == NULL && resolved_name != NULL && errno != EINVAL)
(void) strlcpy(resolved_name, file_name, MAXPATHLEN);
return (ret);
}
/*
* frealpath() is similar to realpath but requires specifying the a size of
* the buffer; if no buffer is given, one will be allocated. If the size is 0,
* the system tries to allocate a buffer large enough to store the result.
* There is no limit to what this function will return in that case.
*/
char *
frealpath(int fd, char *resolved_name, size_t bufsize)
{
return (krealpath_impl(fd, NULL, resolved_name, bufsize));
}
/*
* GNU extension. No limit in what we return.
*/
char *
canonicalize_file_name(const char *path)
{
return (krealpath_impl(AT_FDCWD, path, NULL, 0));
}