summaryrefslogblamecommitdiffstats
path: root/lib/canonicalize.c
blob: 49ac8024661ab9a264644d5321b74a35fda7190f (plain) (tree)
1
2
3
4
5
6
7
8
  
                                                               
  

                                                      
  
                                                      
   


                   
                   
                  
                   

                      

                         
 





                                                                             
                                              




                                                  


                                
                                                                      
                                                  





                                                                      


                                            




                   

















                                                      
                                         
 
                                 
 
                            

                            

                                         

                                    

                                                        



                                        
         
 
                         

 
                                                    
 
                                 



                   
                            










                                                           
                                         
                       
                              






                                                        


                                                     
                                



                            
                         

 
 
                                












                                                                  
/*
 * canonicalize.c -- canonicalize pathname by removing symlinks
 *
 * This file may be distributed under the terms of the
 * GNU Lesser General Public License.
 *
 * Copyright (C) 2009-2013 Karel Zak <kzak@redhat.com>
 */
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "canonicalize.h"

/*
 * Converts private "dm-N" names to "/dev/mapper/<name>"
 *
 * Since 2.6.29 (patch 784aae735d9b0bba3f8b9faef4c8b30df3bf0128) kernel sysfs
 * provides the real DM device names in /sys/block/<ptname>/dm/name
 */
char *canonicalize_dm_name(const char *ptname)
{
	FILE	*f;
	size_t	sz;
	char	path[256], name[256], *res = NULL;

	if (!ptname || !*ptname)
		return NULL;

	snprintf(path, sizeof(path), "/sys/block/%s/dm/name", ptname);
	if (!(f = fopen(path, "r" UL_CLOEXECSTR)))
		return NULL;

	/* read "<name>\n" from sysfs */
	if (fgets(name, sizeof(name), f) && (sz = strlen(name)) > 1) {
		name[sz - 1] = '\0';
		snprintf(path, sizeof(path), "/dev/mapper/%s", name);

		if (access(path, F_OK) == 0)
			res = strdup(path);
	}
	fclose(f);
	return res;
}

static int is_dm_devname(char *canonical, char **name)
{
	struct stat sb;
	char *p = strrchr(canonical, '/');

	*name = NULL;

	if (!p
	    || strncmp(p, "/dm-", 4) != 0
	    || !isdigit(*(p + 4))
	    || stat(canonical, &sb) != 0
	    || !S_ISBLK(sb.st_mode))
		return 0;

	*name = p + 1;
	return 1;
}

char *canonicalize_path(const char *path)
{
	char *canonical, *dmname;

	if (!path || !*path)
		return NULL;

	canonical = realpath(path, NULL);
	if (!canonical)
		return strdup(path);

	if (is_dm_devname(canonical, &dmname)) {
		char *dm = canonicalize_dm_name(dmname);
		if (dm) {
			free(canonical);
			return dm;
		}
	}

	return canonical;
}

char *canonicalize_path_restricted(const char *path)
{
	char *canonical, *dmname;
	int errsv;
	uid_t euid;
	gid_t egid;

	if (!path || !*path)
		return NULL;

	euid = geteuid();
	egid = getegid();

	/* drop permissions */
	if (setegid(getgid()) < 0 || seteuid(getuid()) < 0)
		return NULL;

	errsv = errno = 0;

	canonical = realpath(path, NULL);
	if (!canonical)
		errsv = errno;
	else if (is_dm_devname(canonical, &dmname)) {
		char *dm = canonicalize_dm_name(dmname);
		if (dm) {
			free(canonical);
			canonical = dm;
		}
	}

	/* restore */
	if (setegid(egid) < 0 || seteuid(euid) < 0) {
		free(canonical);
		return NULL;
	}

	errno = errsv;
	return canonical;
}


#ifdef TEST_PROGRAM_CANONICALIZE
int main(int argc, char **argv)
{
	if (argc < 2) {
		fprintf(stderr, "usage: %s <device>\n", argv[0]);
		exit(EXIT_FAILURE);
	}

	fprintf(stdout, "orig: %s\n", argv[1]);
	fprintf(stdout, "real: %s\n", canonicalize_path(argv[1]));

	exit(EXIT_SUCCESS);
}
#endif