123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382 |
- /*++
- Copyright (c) 2015 Minoca Corp.
- This file is licensed under the terms of the GNU General Public License
- version 3. Alternative licensing terms are available. Contact
- info@minocacorp.com for details. See the LICENSE file at the root of this
- project for complete licensing information.
- Module Name:
- realpath.c
- Abstract:
- This module implements support for the realpath function.
- Author:
- Evan Green 9-Mar-2015
- Environment:
- User Mode C Library
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include "libcp.h"
- #include <assert.h>
- #include <errno.h>
- #include <limits.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/param.h>
- #include <sys/stat.h>
- #include <unistd.h>
- //
- // ---------------------------------------------------------------- Definitions
- //
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // ------------------------------------------------------------------ Functions
- //
- LIBC_API
- char *
- realpath (
- const char *Path,
- char *ResolvedPath
- )
- /*++
- Routine Description:
- This routine returns the canonical path for the given file path. This
- canonical path will include no '.' or '..' components, and will not
- contain symbolic links in any components of the path. All path components
- must exist.
- Arguments:
- Path - Supplies a pointer to the path to canonicalize.
- ResolvedPath - Supplies an optional pointer to the buffer to place the
- resolved path in. This must be at least PATH_MAX bytes.
- Return Value:
- Returns a pointer to the resolved path on success.
- NULL on failure.
- --*/
- {
- PSTR AllocatedPath;
- PSTR AppendedLink;
- UINTN ComponentSize;
- PSTR Destination;
- PSTR End;
- UINTN EndLength;
- PSTR Link;
- UINTN LinkCount;
- ssize_t LinkSize;
- PVOID NewBuffer;
- size_t NewSize;
- long PathMax;
- PSTR ResolvedPathEnd;
- PSTR Start;
- struct stat Stat;
- int Status;
- AllocatedPath = NULL;
- AppendedLink = NULL;
- Link = NULL;
- LinkCount = 0;
- if (Path == NULL) {
- errno = EINVAL;
- return NULL;
- }
- if (*Path == '\0') {
- errno = ENOENT;
- return NULL;
- }
- PathMax = PATH_MAX;
- if (ResolvedPath == NULL) {
- AllocatedPath = malloc(PathMax);
- if (AllocatedPath == NULL) {
- return NULL;
- }
- ResolvedPath = AllocatedPath;
- }
- ResolvedPathEnd = ResolvedPath + PathMax;
- //
- // Add the current working directory if this is a relative path.
- //
- if (Path[0] != '/') {
- if (getcwd(ResolvedPath, PathMax) == NULL) {
- Status = errno;
- goto realpathEnd;
- }
- Destination = memchr(ResolvedPath, '\0', PathMax);
- } else {
- ResolvedPath[0] = '/';
- Destination = ResolvedPath + 1;
- }
- Start = (PSTR)Path;
- while (*Start != '\0') {
- //
- // Skip leading separators.
- //
- while (*Start == '/') {
- Start += 1;
- }
- End = Start;
- while ((*End != '\0') && (*End != '/')) {
- End += 1;
- }
- ComponentSize = End - Start;
- if (ComponentSize == 0) {
- break;
- }
- //
- // For dot dot, back up to the previous component.
- //
- if ((ComponentSize == 2) && (Start[0] == '.') && (Start[1] == '.')) {
- if (Destination > ResolvedPath + 1) {
- Destination -= 1;
- while (*(Destination - 1) != '/') {
- Destination -= 1;
- }
- }
- //
- // If it's just a dot, do nothing. Otherwise, it's a component.
- //
- } else if (!((ComponentSize == 1) && (Start[0] == '.'))) {
- if (*(Destination - 1) != '/') {
- *Destination = '/';
- Destination += 1;
- }
- //
- // Handle the component being too big, needing reallocation.
- //
- if (Destination + ComponentSize >= ResolvedPathEnd) {
- //
- // If the buffer was handed in, there is no reallocation.
- //
- if (AllocatedPath == NULL) {
- Status = ENAMETOOLONG;
- if (Destination > ResolvedPath + 1) {
- Destination -= 1;
- }
- *Destination = '\0';
- goto realpathEnd;
- }
- NewSize = ResolvedPathEnd - ResolvedPath;
- if (ComponentSize + 1 > PathMax) {
- NewSize += ComponentSize + 1;
- } else {
- NewSize += PathMax;
- }
- NewBuffer = realloc(AllocatedPath, NewSize);
- if (NewBuffer == NULL) {
- Status = errno;
- goto realpathEnd;
- }
- Destination = NewBuffer + (Destination - ResolvedPath);
- AllocatedPath = NewBuffer;
- ResolvedPath = NewBuffer;
- ResolvedPathEnd = ResolvedPath + NewSize;
- }
- memcpy(Destination, Start, ComponentSize);
- Destination += ComponentSize;
- *Destination = '\0';
- if (lstat(ResolvedPath, &Stat) < 0) {
- Status = errno;
- goto realpathEnd;
- }
- //
- // Follow symbolic links.
- //
- if (S_ISLNK(Stat.st_mode)) {
- if (Link == NULL) {
- Link = malloc(PathMax);
- if (Link == NULL) {
- Status = errno;
- goto realpathEnd;
- }
- }
- LinkCount += 1;
- if (LinkCount > MAXSYMLINKS) {
- Status = ELOOP;
- goto realpathEnd;
- }
- LinkSize = readlink(ResolvedPath, Link, PathMax - 1);
- if (LinkSize < 0) {
- Status = errno;
- goto realpathEnd;
- }
- Link[LinkSize] = '\0';
- //
- // Create another buffer containing the concatenation of the
- // link destination and the rest of the path string.
- //
- if (AppendedLink == NULL) {
- AppendedLink = malloc(PathMax);
- if (AppendedLink == NULL) {
- Status = errno;
- goto realpathEnd;
- }
- }
- EndLength = strlen(End);
- if ((EndLength + LinkSize) >= PathMax) {
- Status = ENAMETOOLONG;
- goto realpathEnd;
- }
- //
- // The loop may have already been through here, which means
- // the end buffer may already point inside this buffer. Hence
- // the need for the more delicate memmove (and the need to do
- // it before copying the link over the beginning part).
- //
- memmove(&(AppendedLink[LinkSize]), End, EndLength + 1);
- memcpy(AppendedLink, Link, LinkSize);
- Path = AppendedLink;
- End = AppendedLink;
- //
- // If it's an absolute link, start there.
- //
- if (Link[0] == '/') {
- Destination = ResolvedPath + 1;
- //
- // If it's a relative link, back up a component.
- //
- } else {
- if (Destination > ResolvedPath + 1) {
- Destination -= 1;
- while (*(Destination - 1) != '/') {
- Destination -= 1;
- }
- }
- }
- //
- // Fail if it's not the final component and it's not a directory.
- // This also catches paths that end in a slash, enforcing that they
- // must also be directories.
- //
- } else if ((!S_ISDIR(Stat.st_mode)) && (*End != '\0')) {
- Status = ENOTDIR;
- goto realpathEnd;
- }
- }
- //
- // Move to the next component.
- //
- Start = End;
- }
- //
- // Remove a trailing slash.
- //
- if ((Destination > ResolvedPath + 1) && (*(Destination - 1) == '/')) {
- Destination -= 1;
- }
- *Destination = '\0';
- Status = 0;
- realpathEnd:
- if (Link != NULL) {
- free(Link);
- }
- if (AppendedLink != NULL) {
- free(AppendedLink);
- }
- assert((ResolvedPath != NULL) &&
- ((AllocatedPath == NULL) || (ResolvedPath == AllocatedPath)));
- if (Status != 0) {
- errno = Status;
- if (AllocatedPath != NULL) {
- free(AllocatedPath);
- AllocatedPath = NULL;
- }
- ResolvedPath = NULL;
- }
- return ResolvedPath;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
|