summaryrefslogblamecommitdiffstats
path: root/lib/canonicalize.c
blob: b888fbb8e9be629d56fa4726ea081b4dce9c1053 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
  
                                                               










                                                                       
  


   

                                                             

                                                         
   
                   
                   
                  


                         
 


                        
 
             
                                                                  
                          


                                   
                         
 
                              
 
                                                                  
                           

                                                




                                       

                       
 

                                                             
                                      



                                   












                                                                          


                                                              
                                                                
                                                     
                                         
                         
                                           
                 
 
                                                     
                                                
                                      
                                 
                 
 
                                                                  

                                                                 


                                                                               
                                         
                        
                              
                                     
 



                                                                         
                                                      

                                                                            
                                                         
                                         
 
                                                                
                                         


                                                   

                                                        
                                  
                                            
                 
                               

                                                                 

                                                         
                                             
                      
 
                  
                             

     
                  
                    
 














                                                     
/*
 * canonicalize.c -- canonicalize pathname by removing symlinks
 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Library Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library Public License for more details.
 *
 */

/*
 * This routine is part of libc.  We include it nevertheless,
 * since the libc version has some security flaws.
 *
 * TODO: use canonicalize_file_name() when exist in glibc
 */
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>

#include "canonicalize.h"

#ifndef MAXSYMLINKS
# define MAXSYMLINKS 256
#endif

static char *
myrealpath(const char *path, char *resolved_path, int maxreslth) {
	int readlinks = 0;
	char *npath;
	char link_path[PATH_MAX+1];
	int n;
	char *buf = NULL;

	npath = resolved_path;

	/* If it's a relative pathname use getcwd for starters. */
	if (*path != '/') {
		if (!getcwd(npath, maxreslth-2))
			return NULL;
		npath += strlen(npath);
		if (npath[-1] != '/')
			*npath++ = '/';
	} else {
		*npath++ = '/';
		path++;
	}

	/* Expand each slash-separated pathname component. */
	while (*path != '\0') {
		/* Ignore stray "/" */
		if (*path == '/') {
			path++;
			continue;
		}
		if (*path == '.' && (path[1] == '\0' || path[1] == '/')) {
			/* Ignore "." */
			path++;
			continue;
		}
		if (*path == '.' && path[1] == '.' &&
		    (path[2] == '\0' || path[2] == '/')) {
			/* Backup for ".." */
			path += 2;
			while (npath > resolved_path+1 &&
			       (--npath)[-1] != '/')
				;
			continue;
		}
		/* Safely copy the next pathname component. */
		while (*path != '\0' && *path != '/') {
			if (npath-resolved_path > maxreslth-2) {
				errno = ENAMETOOLONG;
				goto err;
			}
			*npath++ = *path++;
		}

		/* Protect against infinite loops. */
		if (readlinks++ > MAXSYMLINKS) {
			errno = ELOOP;
			goto err;
		}

		/* See if last pathname component is a symlink. */
		*npath = '\0';
		n = readlink(resolved_path, link_path, PATH_MAX);
		if (n < 0) {
			/* EINVAL means the file exists but isn't a symlink. */
			if (errno != EINVAL)
				goto err;
		} else {
			int m;
			char *newbuf;

			/* Note: readlink doesn't add the null byte. */
			link_path[n] = '\0';
			if (*link_path == '/')
				/* Start over for an absolute symlink. */
				npath = resolved_path;
			else
				/* Otherwise back up over this component. */
				while (*(--npath) != '/')
					;

			/* Insert symlink contents into path. */
			m = strlen(path);
			newbuf = malloc(m + n + 1);
			if (!newbuf)
				goto err;
			memcpy(newbuf, link_path, n);
			memcpy(newbuf + n, path, m + 1);
			free(buf);
			path = buf = newbuf;
		}
		*npath++ = '/';
	}
	/* Delete trailing slash but don't whomp a lone slash. */
	if (npath != resolved_path+1 && npath[-1] == '/')
		npath--;
	/* Make sure it's null terminated. */
	*npath = '\0';

	free(buf);
	return resolved_path;

 err:
	free(buf);
	return NULL;
}

char *
canonicalize_path(const char *path) {
	char canonical[PATH_MAX+2];

	if (path == NULL)
		return NULL;

	if (myrealpath (path, canonical, PATH_MAX+1))
		return strdup(canonical);

	return strdup(path);
}