summaryrefslogblamecommitdiffstats
path: root/mount/realpath.c
blob: f0d7c76c0184ec26ccbd3577aae0315dfbf9ca30 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16















                                                                       

                                                             

   
                                                  
                   
                   
                  

                                                 


                        



                                                                  


                          
                              
 
                                                                  
                           





                                           

                       
 

                                                             
                                      



                                   












                                                                          


                                                              
                                                                


                                                     
                                           
                 
 




                                                     
 
                                                                    

                                                                 



                                                                               
                        



                                                                         
                                                      

                                                                            
                                                         

                                             
                                                                    




                                                                
                                                  
                 

                               

                                                                 

                                                         
                                             
                      

                             
/*
 * realpath.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.
 */

#include <limits.h>		/* for PATH_MAX */
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include "realpath.h"
#include "sundries.h"		/* for xstrdup */

#define MAX_READLINKS 32

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

	npath = resolved_path;

	/* If it's a relative pathname use getcwd for starters. */
	if (*path != '/') {
		getcwd(npath, maxreslth-2);
		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;
				return NULL;
			}
			*npath++ = *path++;
		}

		/* Protect against infinite loops. */
		if (readlinks++ > MAX_READLINKS) {
			errno = ELOOP;
			return NULL;
		}

		/* See if latest 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)
				return NULL;
		} else {
			/* 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) != '/')
					;
			/* Safe sex check. */
			if (strlen(path) + n >= sizeof(link_path)) {
				errno = ENAMETOOLONG;
				return NULL;
			}
			/* Insert symlink contents into path. */
			strcat(link_path, path);
			path = xstrdup(link_path);
		}

		*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';
	return resolved_path;
}