mirror of
https://github.com/php/php-src.git
synced 2024-11-25 10:54:15 +08:00
284 lines
7.6 KiB
C
284 lines
7.6 KiB
C
/*
|
|
+----------------------------------------------------------------------+
|
|
| PHP version 4.0 |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 1997, 1998, 1999, 2000 The PHP Group |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 2.02 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available at through the world-wide-web at |
|
|
| http://www.php.net/license/2_02.txt. |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
| Author: Sander Steffann (sander@steffann.nl) |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include "php.h"
|
|
|
|
#if HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
#include <sys/stat.h>
|
|
|
|
#ifndef MAXSYMLINKS
|
|
#define MAXSYMLINKS 32
|
|
#endif
|
|
|
|
#ifndef S_ISDIR
|
|
#define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR)
|
|
#endif
|
|
|
|
char *php_realpath(char *path, char resolved_path[]);
|
|
|
|
#ifdef PHP_WIN32
|
|
#define IS_SLASH(p) ((p) == '/' || (p) == '\\')
|
|
#else
|
|
#define IS_SLASH(p) ((p) == '/')
|
|
#endif
|
|
|
|
char *php_realpath(char *path, char resolved_path []) {
|
|
char path_construction[MAXPATHLEN]; /* We build the result in here */
|
|
char *writepos; /* Position to write next char */
|
|
|
|
char path_copy[MAXPATHLEN]; /* A work-copy of the path */
|
|
char *workpos; /* working position in *path */
|
|
|
|
#if !defined(PHP_WIN32)
|
|
char buf[MAXPATHLEN]; /* Buffer for readlink */
|
|
int linklength; /* The result from readlink */
|
|
#endif
|
|
int linkcount = 0; /* Count symlinks to avoid loops */
|
|
|
|
struct stat filestat; /* result from stat */
|
|
|
|
#ifdef PHP_WIN32
|
|
char *temppos; /* position while counting '.' */
|
|
int dotcount; /* number of '.' */
|
|
int t; /* counter */
|
|
#endif
|
|
|
|
/* Set the work-position to the beginning of the given path */
|
|
strcpy(path_copy, path);
|
|
workpos = path_copy;
|
|
|
|
#ifdef PHP_WIN32
|
|
/* Find out where we start - Windows version */
|
|
if (IS_SLASH(*workpos)) {
|
|
/* We start at the root of the current drive */
|
|
/* Get the current directory */
|
|
if (V_GETCWD(path_construction, MAXPATHLEN-1) == NULL) {
|
|
/* Unable to get cwd */
|
|
resolved_path[0] = 0;
|
|
return NULL;
|
|
}
|
|
/* We only need the first three chars (for example "C:\") */
|
|
path_construction[3] = 0;
|
|
workpos++;
|
|
} else if (workpos[1] == ':') {
|
|
/* A drive-letter is specified, copy it */
|
|
strncpy(path_construction, path, 2);
|
|
strcat(path_construction, "\\");
|
|
workpos++;
|
|
workpos++;
|
|
} else {
|
|
/* Use the current directory */
|
|
if (V_GETCWD(path_construction, MAXPATHLEN-1) == NULL) {
|
|
/* Unable to get cwd */
|
|
resolved_path[0] = 0;
|
|
return NULL;
|
|
}
|
|
strcat(path_construction, "\\");
|
|
}
|
|
#else
|
|
/* Find out where we start - Unix version */
|
|
if (*workpos == '/') {
|
|
/* We start at the root */
|
|
strcpy(path_construction, "/");
|
|
workpos++;
|
|
} else {
|
|
/* Use the current directory */
|
|
if (V_GETCWD(path_construction, MAXPATHLEN-1) == NULL) {
|
|
/* Unable to get cwd */
|
|
resolved_path[0] = 0;
|
|
return NULL;
|
|
}
|
|
strcat(path_construction, "/");
|
|
}
|
|
#endif
|
|
|
|
/* Set the next-char-position */
|
|
writepos = &path_construction[strlen(path_construction)];
|
|
|
|
/* Go to the end, then stop */
|
|
while(*workpos != 0) {
|
|
/* Strip (back)slashes */
|
|
while(IS_SLASH(*workpos)) workpos++;
|
|
|
|
#ifdef PHP_WIN32
|
|
/* reset dotcount */
|
|
dotcount = 0;
|
|
|
|
/* Look for .. */
|
|
if ((workpos[0] == '.') && (workpos[1] != 0)) {
|
|
/* Windows accepts \...\ as \..\..\, \....\ as \..\..\..\, etc */
|
|
/* At least Win98 does */
|
|
|
|
temppos = workpos;
|
|
while(*temppos++ == '.') {
|
|
dotcount++;
|
|
if (!IS_SLASH(*temppos) && (*temppos != 0) && (*temppos != '.')) {
|
|
/* This is not a /../ component, but a filename that starts with '.' */
|
|
dotcount = 0;
|
|
}
|
|
}
|
|
|
|
/* Go back dotcount-1 times */
|
|
for (t=0 ; t<(dotcount-1) ; t++) {
|
|
workpos++; /* move to next '.' */
|
|
|
|
/* Can we still go back? */
|
|
if ((writepos-3) <= path_construction) return NULL;
|
|
|
|
/* Go back */
|
|
writepos--; /* move to '\' */
|
|
writepos--;
|
|
while(!IS_SLASH(*writepos)) writepos--; /* skip until previous '\\' */
|
|
}
|
|
workpos++;
|
|
}
|
|
|
|
/* No special case */
|
|
if (dotcount == 0) {
|
|
/* Append */
|
|
while(!IS_SLASH(*workpos) && (*workpos != 0)) {
|
|
*writepos++ = *workpos++;
|
|
}
|
|
}
|
|
|
|
/* Just one '.', go to next element */
|
|
if (dotcount == 1) {
|
|
while(!IS_SLASH(*workpos) && (*workpos != 0)) {
|
|
*workpos++;
|
|
}
|
|
|
|
/* Avoid double \ in the result */
|
|
writepos--;
|
|
}
|
|
|
|
/* If it was a directory, append a slash */
|
|
if (IS_SLASH(*workpos)) {
|
|
*writepos++ = *workpos++;
|
|
}
|
|
*writepos = 0;
|
|
#else /* defined(PHP_WIN32) */
|
|
/* Look for .. */
|
|
if ((workpos[0] == '.') && (workpos[1] != 0)) {
|
|
if ((workpos[1] == '.') && ((workpos[2] == '/') || (workpos[2] == 0))) {
|
|
/* One directory back */
|
|
/* Set pointers to right position */
|
|
workpos++; /* move to second '.' */
|
|
workpos++; /* move to '/' */
|
|
|
|
/* Only apply .. if not in root */
|
|
if ((writepos-1) > path_construction) {
|
|
writepos--; /* move to '/' */
|
|
while(*--writepos != '/') ; /* skip until previous '/' */
|
|
}
|
|
} else {
|
|
if (workpos[1] == '/') {
|
|
/* Found a /./ skip it */
|
|
workpos++; /* move to '/' */
|
|
|
|
/* Avoid double / in the result */
|
|
writepos--;
|
|
} else {
|
|
/* No special case, the name just started with a . */
|
|
/* Append */
|
|
while((*workpos != '/') && (*workpos != 0)) {
|
|
*writepos++ = *workpos++;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
/* No special case */
|
|
/* Append */
|
|
while((*workpos != '/') && (*workpos != 0)) {
|
|
*writepos++ = *workpos++;
|
|
}
|
|
}
|
|
|
|
#if HAVE_SYMLINK
|
|
/* We are going to use path_construction, so close it */
|
|
*writepos = 0;
|
|
|
|
/* Check the current location to see if it is a symlink */
|
|
if((linklength = readlink(path_construction, buf, MAXPATHLEN)) != -1) {
|
|
/* Check linkcount */
|
|
if (linkcount > MAXSYMLINKS) return NULL;
|
|
|
|
/* Count this symlink */
|
|
linkcount++;
|
|
|
|
/* Set end of buf */
|
|
buf[linklength] = 0;
|
|
|
|
/* Check for overflow */
|
|
if ((strlen(workpos) + strlen(buf) + 1) >= MAXPATHLEN) return NULL;
|
|
|
|
/* Remove the symlink-component wrom path_construction */
|
|
writepos--; /* move to '/' */
|
|
while(*--writepos != '/') ; /* skip until previous '/' */
|
|
*++writepos = 0; /* end of string after '/' */
|
|
|
|
/* If the symlink starts with a '/', empty path_construction */
|
|
if (*buf == '/') {
|
|
*path_construction = 0;
|
|
writepos = path_construction;
|
|
}
|
|
|
|
/* Insert symlink into path_copy */
|
|
strcat(buf, workpos);
|
|
strcpy(path_copy, buf);
|
|
workpos = path_copy;
|
|
}
|
|
#endif /* HAVE_SYMLINK */
|
|
|
|
/* If it was a directory, append a slash */
|
|
if (*workpos == '/') {
|
|
*writepos++ = *workpos++;
|
|
}
|
|
*writepos = 0;
|
|
#endif /* defined(PHP_WIN32) */
|
|
}
|
|
|
|
/* Check if the resolved path is a directory */
|
|
if (V_STAT(path_construction, &filestat) != 0) {
|
|
if (errno != ENOENT) return NULL;
|
|
} else {
|
|
if (S_ISDIR(filestat.st_mode)) {
|
|
/* It's a directory, append a / if needed */
|
|
if (*(writepos-1) != '/') {
|
|
/* Check for overflow */
|
|
if ((strlen(workpos) + 2) >= MAXPATHLEN) {
|
|
return NULL;
|
|
}
|
|
*writepos++ = '/';
|
|
*writepos = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
strcpy(resolved_path, path_construction);
|
|
return resolved_path;
|
|
}
|
|
|
|
/*
|
|
* Local variables:
|
|
* tab-width: 4
|
|
* c-basic-offset: 4
|
|
* End:
|
|
*/
|