diff options
| -rw-r--r-- | libc/stdlib/realpath.c | 70 | 
1 files changed, 36 insertions, 34 deletions
| diff --git a/libc/stdlib/realpath.c b/libc/stdlib/realpath.c index aae8580a5..99afbde0c 100644 --- a/libc/stdlib/realpath.c +++ b/libc/stdlib/realpath.c @@ -43,20 +43,23 @@ libc_hidden_proto(getcwd)  #define MAX_READLINKS 32  #ifdef __STDC__ -char *realpath(const char *path, char resolved_path[]) +char *realpath(const char *path, char got_path[])  #else -char *realpath(path, resolved_path) +char *realpath(path, got_path)  const char *path; -char resolved_path[]; +char got_path[];  #endif  {  	char copy_path[PATH_MAX]; -	char link_path[PATH_MAX]; -	char got_path[PATH_MAX]; -	char *new_path = got_path; +	/* use user supplied buffer directly - reduces stack usage */ +	/* char got_path[PATH_MAX]; */  	char *max_path; +	char *new_path; +	size_t path_len;  	int readlinks = 0; -	int n; +#ifdef S_IFLNK +	int link_len; +#endif  	if (path == NULL) {  		__set_errno(EINVAL); @@ -67,17 +70,20 @@ char resolved_path[];  		return NULL;  	}  	/* Make a copy of the source path since we may need to modify it. */ -	if (strlen(path) >= PATH_MAX - 2) { +	path_len = strlen(path); +	if (path_len >= PATH_MAX - 2) {  		__set_errno(ENAMETOOLONG);  		return NULL;  	} -	strcpy(copy_path, path); -	path = copy_path; -	max_path = copy_path + PATH_MAX - 2; -	/* If it's a relative pathname use getcwd for starters. */ +	/* Copy so that path is at the end of copy_path[] */ +	strcpy(copy_path + (PATH_MAX-1) - path_len, path); +	path = copy_path + (PATH_MAX-1) - path_len; +	max_path = got_path + PATH_MAX - 2; /* points to last non-NUL char */ +	new_path = got_path;  	if (*path != '/') { -		/* Ohoo... */ -		getcwd(new_path, PATH_MAX - 1); +		/* If it's a relative pathname use getcwd for starters. */ +		if (!getcwd(new_path, PATH_MAX - 1)) +			return NULL;  		new_path += strlen(new_path);  		if (new_path[-1] != '/')  			*new_path++ = '/'; @@ -112,7 +118,7 @@ char resolved_path[];  		}  		/* Safely copy the next pathname component. */  		while (*path != '\0' && *path != '/') { -			if (path > max_path) { +			if (new_path > max_path) {  				__set_errno(ENAMETOOLONG);  				return NULL;  			} @@ -124,35 +130,32 @@ char resolved_path[];  			__set_errno(ELOOP);  			return NULL;  		} -		/* See if latest pathname component is a symlink. */ +		path_len = strlen(path); +		/* See if last (so far) pathname component is a symlink. */  		*new_path = '\0'; -		n = readlink(got_path, link_path, PATH_MAX - 1); -		if (n < 0) { +		link_len = readlink(got_path, copy_path, PATH_MAX - 1); +		if (link_len < 0) {  			/* EINVAL means the file exists but isn't a symlink. */  			if (errno != EINVAL) { -				/* Make sure it's null terminated. */ -				*new_path = '\0'; -				strcpy(resolved_path, got_path);  				return NULL;  			}  		} else { +			/* Safe sex check. */ +			if (path_len + link_len >= PATH_MAX - 2) { +				__set_errno(ENAMETOOLONG); +				return NULL; +			}  			/* Note: readlink doesn't add the null byte. */ -			link_path[n] = '\0'; -			if (*link_path == '/') +			/* copy_path[link_len] = '\0'; - we don't need it too */ +			if (*copy_path == '/')  				/* Start over for an absolute symlink. */  				new_path = got_path;  			else  				/* Otherwise back up over this component. */  				while (*(--new_path) != '/'); -			/* Safe sex check. */ -			if (strlen(path) + n >= PATH_MAX - 2) { -				__set_errno(ENAMETOOLONG); -				return NULL; -			} -			/* Insert symlink contents into path. */ -			strcat(link_path, path); -			strcpy(copy_path, link_path); -			path = copy_path; +			/* Prepend symlink contents to path. */ +			memmove(copy_path + (PATH_MAX-1) - link_len - path_len, copy_path, link_len); +			path = copy_path + (PATH_MAX-1) - link_len - path_len;  		}  #endif							/* S_IFLNK */  		*new_path++ = '/'; @@ -162,6 +165,5 @@ char resolved_path[];  		new_path--;  	/* Make sure it's null terminated. */  	*new_path = '\0'; -	strcpy(resolved_path, got_path); -	return resolved_path; +	return got_path;  } | 
