diff options
| author | Max Khon <fjoe@FreeBSD.org> | 2003-03-29 21:56:59 +0000 | 
|---|---|---|
| committer | Max Khon <fjoe@FreeBSD.org> | 2003-03-29 21:56:59 +0000 | 
| commit | 839e119ec83e480e5b1a93513487655069eaba5c (patch) | |
| tree | f2e6d2181f6d6a21de1ea08a43deea84522cb56f /lib/libc/stdlib/realpath.c | |
| parent | bb25efadc4091116b09b1566225744fa6851fa00 (diff) | |
Notes
Diffstat (limited to 'lib/libc/stdlib/realpath.c')
| -rw-r--r-- | lib/libc/stdlib/realpath.c | 168 | 
1 files changed, 89 insertions, 79 deletions
| diff --git a/lib/libc/stdlib/realpath.c b/lib/libc/stdlib/realpath.c index 56ece3a6aa78..c2694717a9e8 100644 --- a/lib/libc/stdlib/realpath.c +++ b/lib/libc/stdlib/realpath.c @@ -37,151 +37,161 @@ __FBSDID("$FreeBSD$");  #include <sys/stat.h>  #include <errno.h> +#include <stdlib.h>  #include <string.h>  #include <unistd.h>  #include "un-namespace.h"  /* - * char *realpath(const char *path, char resolved_path[PATH_MAX]); + * char *realpath(const char *path, char resolved[PATH_MAX]);   *   * Find the real name of path, by removing all ".", ".." and symlink   * components.  Returns (resolved) on success, or (NULL) on failure,   * in which case the path which caused trouble is left in (resolved).   */  char * -realpath(const char *path, char resolved_path[PATH_MAX]) +realpath(const char *path, char resolved[PATH_MAX])  { -	unsigned num_symlinks = 0; -	int saved_errno = errno; - -	char left[PATH_MAX]; +	struct stat sb; +	char *p, *q, *s;  	size_t left_len, resolved_len; +	unsigned symlinks; +	int serrno, slen; +	char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX]; +	serrno = errno; +	symlinks = 0;  	if (path[0] == '/') { -		resolved_path[0] = '/'; -		resolved_path[1] = '\0'; +		resolved[0] = '/'; +		resolved[1] = '\0';  		if (path[1] == '\0') -			return resolved_path; +			return (resolved);  		resolved_len = 1; -		left_len = strlcpy(left, path + 1, PATH_MAX); +		left_len = strlcpy(left, path + 1, sizeof(left));  	} else { -		if (getcwd(resolved_path, PATH_MAX) == NULL) { -			strlcpy(resolved_path, ".", PATH_MAX); -			return NULL; +		if (getcwd(resolved, PATH_MAX) == NULL) { +			strlcpy(resolved, ".", PATH_MAX); +			return (NULL);  		} -		resolved_len = strlen(resolved_path); -		left_len = strlcpy(left, path, PATH_MAX); +		resolved_len = strlen(resolved); +		left_len = strlcpy(left, path, sizeof(left));  	} -	if (left_len >= PATH_MAX || resolved_len >= PATH_MAX) { +	if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) {  		errno = ENAMETOOLONG; -		return NULL; +		return (NULL);  	} -	while (left_len > 0) { -		struct stat st; -		char next_token[PATH_MAX]; -		char *p; -		char *s = (p = strchr(left, '/')) ? p : left + left_len; - -		memmove(next_token, left, s - left); +	/* +	 * Iterate over path components in `left'. +	 */ +	while (left_len != 0) { +		/* +		 * Extract the next path component and adjust `left' +		 * and its length. +		 */ +		p = strchr(left, '/'); +		s = p ? p : left + left_len; +		if (s - left >= sizeof(next_token)) { +			errno = ENAMETOOLONG; +			return (NULL); +		} +		memcpy(next_token, left, s - left); +		next_token[s - left] = '\0';  		left_len -= s - left;  		if (p != NULL)  			memmove(left, s + 1, left_len + 1); - -		next_token[s - left] = '\0'; -		if (resolved_path[resolved_len - 1] != '/') { +		if (resolved[resolved_len - 1] != '/') {  			if (resolved_len + 1 >= PATH_MAX) {  				errno = ENAMETOOLONG; -				return NULL; +				return (NULL);  			} - -			resolved_path[resolved_len++] = '/'; -			resolved_path[resolved_len] = '\0'; +			resolved[resolved_len++] = '/'; +			resolved[resolved_len] = '\0';  		} -  		if (next_token[0] == '\0')  			continue; -		else if (!strcmp(next_token, ".")) +		else if (strcmp(next_token, ".") == 0)  			continue; -		else if (!strcmp(next_token, "..")) { +		else if (strcmp(next_token, "..") == 0) { +			/* +			 * Strip the last path component except when we have +			 * single "/" +			 */  			if (resolved_len > 1) { -				char *q; - -				/* trailing slash */ -				resolved_path[resolved_len - 1] = '\0'; - -				q = strrchr(resolved_path, '/'); +				resolved[resolved_len - 1] = '\0'; +				q = strrchr(resolved, '/');  				*q = '\0'; -				resolved_len = q - resolved_path; +				resolved_len = q - resolved;  			}  			continue;  		} -		/* filename */ -		resolved_len = strlcat(resolved_path, next_token, PATH_MAX); +		/* +		 * Append the next path component and lstat() it. If +		 * lstat() fails we still can return successfully if +		 * there are no more path components left. +		 */ +		resolved_len = strlcat(resolved, next_token, PATH_MAX);  		if (resolved_len >= PATH_MAX) {  			errno = ENAMETOOLONG; -			return NULL; +			return (NULL);  		} - -		if (lstat(resolved_path, &st) < 0) { +		if (lstat(resolved, &sb) != 0) {  			if (errno == ENOENT && p == NULL) { -				errno = saved_errno; -				return resolved_path; +				errno = serrno; +				return (resolved);  			} - -			return NULL; +			return (NULL);  		} - -		if ((st.st_mode & S_IFLNK) == S_IFLNK) { -			char symlink[PATH_MAX]; -			int slen; - -			if (num_symlinks++ > MAXSYMLINKS) { +		if (S_ISLNK(sb.st_mode)) { +			if (symlinks++ > MAXSYMLINKS) {  				errno = ELOOP; -				return NULL; +				return (NULL);  			} -			slen = readlink(resolved_path, symlink, PATH_MAX - 1); +			slen = readlink(resolved, symlink, sizeof(symlink) - 1);  			if (slen < 0) -				return NULL; +				return (NULL);  			symlink[slen] = '\0'; -  			if (symlink[0] == '/') { -				/* absolute link */ -				resolved_path[1] = 0; +				resolved[1] = 0;  				resolved_len = 1;  			} else if (resolved_len > 1) { -				char *q; - -				/* trailing slash */ -				resolved_path[resolved_len - 1] = '\0'; - -				q = strrchr(resolved_path, '/'); +				/* Strip the last path component. */ +				resolved[resolved_len - 1] = '\0'; +				q = strrchr(resolved, '/');  				*q = '\0'; -				resolved_len = q - resolved_path; +				resolved_len = q - resolved;  			} +			/* +			 * If there are any path components left, then +			 * append them to symlink. The result is placed +			 * in `left'. +			 */  			if (p != NULL) {  				if (symlink[slen - 1] != '/') { -					if (slen + 1 >= PATH_MAX) { +					if (slen + 1 >= sizeof(symlink)) {  						errno = ENAMETOOLONG; -						return NULL; +						return (NULL);  					}  					symlink[slen] = '/';  					symlink[slen + 1] = 0;  				} -				left_len = strlcat(symlink, left, PATH_MAX); -				if (left_len >= PATH_MAX) { +				left_len = strlcat(symlink, left, sizeof(left)); +				if (left_len >= sizeof(left)) {  					errno = ENAMETOOLONG; -					return NULL; +					return (NULL);  				}  			} -			left_len = strlcpy(left, symlink, PATH_MAX); +			left_len = strlcpy(left, symlink, sizeof(left));  		}  	} -	if (resolved_len > 1 && resolved_path[resolved_len - 1] == '/') -		resolved_path[resolved_len - 1] = '\0'; - -	return resolved_path; +	/* +	 * Remove trailing slash except when the resolved pathname +	 * is a single "/". +	 */ +	if (resolved_len > 1 && resolved[resolved_len - 1] == '/') +		resolved[resolved_len - 1] = '\0'; +	return (resolved);  } | 
