1/* Code to mangle pathnames into those matching a given prefix. 2 eg. open("/lib/foo.so") => open("/usr/gnemul/i386-linux/lib/foo.so"); 3 4 The assumption is that this area does not change. 5*/ 6#ifdef __linux__ 7#define _GNU_SOURCE 1 8#endif 9#include <sys/types.h> 10#include <sys/param.h> 11#include <dirent.h> 12#include <unistd.h> 13#include <stdlib.h> 14#include <string.h> 15#include <errno.h> 16#include <stdio.h> 17#include "qemu-common.h" 18 19struct pathelem 20{ 21 /* Name of this, eg. lib */ 22 char *name; 23 /* Full path name, eg. /usr/gnemul/x86-linux/lib. */ 24 char *pathname; 25 struct pathelem *parent; 26 /* Children */ 27 unsigned int num_entries; 28 struct pathelem *entries[0]; 29}; 30 31static struct pathelem *base; 32 33/* First N chars of S1 match S2, and S2 is N chars long. */ 34static int strneq(const char *s1, unsigned int n, const char *s2) 35{ 36 unsigned int i; 37 38 for (i = 0; i < n; i++) 39 if (s1[i] != s2[i]) 40 return 0; 41 return s2[i] == 0; 42} 43 44static struct pathelem *add_entry(struct pathelem *root, const char *name, 45 unsigned type); 46 47static struct pathelem *new_entry(const char *root, 48 struct pathelem *parent, 49 const char *name) 50{ 51 struct pathelem *new = malloc(sizeof(*new)); 52 new->name = strdup(name); 53 new->pathname = g_strdup_printf("%s/%s", root, name); 54 new->num_entries = 0; 55 return new; 56} 57 58#define streq(a,b) (strcmp((a), (b)) == 0) 59 60/* Not all systems provide this feature */ 61#if defined(DT_DIR) && defined(DT_UNKNOWN) && defined(DT_LNK) 62# define dirent_type(dirent) ((dirent)->d_type) 63# define is_dir_maybe(type) \ 64 ((type) == DT_DIR || (type) == DT_UNKNOWN || (type) == DT_LNK) 65#else 66# define dirent_type(dirent) (1) 67# define is_dir_maybe(type) (type) 68#endif 69 70static struct pathelem *add_dir_maybe(struct pathelem *path) 71{ 72 DIR *dir; 73 74 if ((dir = opendir(path->pathname)) != NULL) { 75 struct dirent *dirent; 76 77 while ((dirent = readdir(dir)) != NULL) { 78 if (!streq(dirent->d_name,".") && !streq(dirent->d_name,"..")){ 79 path = add_entry(path, dirent->d_name, dirent_type(dirent)); 80 } 81 } 82 closedir(dir); 83 } 84 return path; 85} 86 87static struct pathelem *add_entry(struct pathelem *root, const char *name, 88 unsigned type) 89{ 90 struct pathelem **e; 91 92 root->num_entries++; 93 94 root = realloc(root, sizeof(*root) 95 + sizeof(root->entries[0])*root->num_entries); 96 e = &root->entries[root->num_entries-1]; 97 98 *e = new_entry(root->pathname, root, name); 99 if (is_dir_maybe(type)) { 100 *e = add_dir_maybe(*e); 101 } 102 103 return root; 104} 105 106/* This needs to be done after tree is stabilized (ie. no more reallocs!). */ 107static void set_parents(struct pathelem *child, struct pathelem *parent) 108{ 109 unsigned int i; 110 111 child->parent = parent; 112 for (i = 0; i < child->num_entries; i++) 113 set_parents(child->entries[i], child); 114} 115 116/* FIXME: Doesn't handle DIR/.. where DIR is not in emulated dir. */ 117static const char * 118follow_path(const struct pathelem *cursor, const char *name) 119{ 120 unsigned int i, namelen; 121 122 name += strspn(name, "/"); 123 namelen = strcspn(name, "/"); 124 125 if (namelen == 0) 126 return cursor->pathname; 127 128 if (strneq(name, namelen, "..")) 129 return follow_path(cursor->parent, name + namelen); 130 131 if (strneq(name, namelen, ".")) 132 return follow_path(cursor, name + namelen); 133 134 for (i = 0; i < cursor->num_entries; i++) 135 if (strneq(name, namelen, cursor->entries[i]->name)) 136 return follow_path(cursor->entries[i], name + namelen); 137 138 /* Not found */ 139 return NULL; 140} 141 142void init_paths(const char *prefix) 143{ 144 char pref_buf[PATH_MAX]; 145 146 if (prefix[0] == '\0' || 147 !strcmp(prefix, "/")) 148 return; 149 150 if (prefix[0] != '/') { 151 char *cwd = getcwd(NULL, 0); 152 size_t pref_buf_len = sizeof(pref_buf); 153 154 if (!cwd) 155 abort(); 156 pstrcpy(pref_buf, sizeof(pref_buf), cwd); 157 pstrcat(pref_buf, pref_buf_len, "/"); 158 pstrcat(pref_buf, pref_buf_len, prefix); 159 free(cwd); 160 } else 161 pstrcpy(pref_buf, sizeof(pref_buf), prefix + 1); 162 163 base = new_entry("", NULL, pref_buf); 164 base = add_dir_maybe(base); 165 if (base->num_entries == 0) { 166 free (base); 167 base = NULL; 168 } else { 169 set_parents(base, base); 170 } 171} 172 173/* Look for path in emulation dir, otherwise return name. */ 174const char *path(const char *name) 175{ 176 /* Only do absolute paths: quick and dirty, but should mostly be OK. 177 Could do relative by tracking cwd. */ 178 if (!base || !name || name[0] != '/') 179 return name; 180 181 return follow_path(base, name) ?: name; 182} 183