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 19 struct 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 31 static struct pathelem *base; 32 33 /* First N chars of S1 match S2, and S2 is N chars long. */ 34 static 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 44 static struct pathelem *add_entry(struct pathelem *root, const char *name, 45 unsigned char type); 46 47 static 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 if (asprintf(&new->pathname, "%s/%s", root, name) == -1) { 54 printf("Cannot allocate memory\n"); 55 exit(1); 56 } 57 new->num_entries = 0; 58 return new; 59 } 60 61 #define streq(a,b) (strcmp((a), (b)) == 0) 62 63 /* Not all systems provide this feature */ 64 #if defined(DT_DIR) && defined(DT_UNKNOWN) 65 # define dirent_type(dirent) ((dirent)->d_type) 66 # define is_dir_maybe(type) ((type) == DT_DIR || (type) == DT_UNKNOWN) 67 #else 68 # define dirent_type(dirent) (1) 69 # define is_dir_maybe(type) (type) 70 #endif 71 72 static struct pathelem *add_dir_maybe(struct pathelem *path) 73 { 74 DIR *dir; 75 76 if ((dir = opendir(path->pathname)) != NULL) { 77 struct dirent *dirent; 78 79 while ((dirent = readdir(dir)) != NULL) { 80 if (!streq(dirent->d_name,".") && !streq(dirent->d_name,"..")){ 81 path = add_entry(path, dirent->d_name, dirent_type(dirent)); 82 } 83 } 84 closedir(dir); 85 } 86 return path; 87 } 88 89 static struct pathelem *add_entry(struct pathelem *root, const char *name, 90 unsigned char type) 91 { 92 struct pathelem **e; 93 94 root->num_entries++; 95 96 root = realloc(root, sizeof(*root) 97 + sizeof(root->entries[0])*root->num_entries); 98 e = &root->entries[root->num_entries-1]; 99 100 *e = new_entry(root->pathname, root, name); 101 if (is_dir_maybe(type)) { 102 *e = add_dir_maybe(*e); 103 } 104 105 return root; 106 } 107 108 /* This needs to be done after tree is stabilized (ie. no more reallocs!). */ 109 static void set_parents(struct pathelem *child, struct pathelem *parent) 110 { 111 unsigned int i; 112 113 child->parent = parent; 114 for (i = 0; i < child->num_entries; i++) 115 set_parents(child->entries[i], child); 116 } 117 118 /* FIXME: Doesn't handle DIR/.. where DIR is not in emulated dir. */ 119 static const char * 120 follow_path(const struct pathelem *cursor, const char *name) 121 { 122 unsigned int i, namelen; 123 124 name += strspn(name, "/"); 125 namelen = strcspn(name, "/"); 126 127 if (namelen == 0) 128 return cursor->pathname; 129 130 if (strneq(name, namelen, "..")) 131 return follow_path(cursor->parent, name + namelen); 132 133 if (strneq(name, namelen, ".")) 134 return follow_path(cursor, name + namelen); 135 136 for (i = 0; i < cursor->num_entries; i++) 137 if (strneq(name, namelen, cursor->entries[i]->name)) 138 return follow_path(cursor->entries[i], name + namelen); 139 140 /* Not found */ 141 return NULL; 142 } 143 144 void init_paths(const char *prefix) 145 { 146 char pref_buf[PATH_MAX]; 147 148 if (prefix[0] == '\0' || 149 !strcmp(prefix, "/")) 150 return; 151 152 if (prefix[0] != '/') { 153 char *cwd = getcwd(NULL, 0); 154 size_t pref_buf_len = sizeof(pref_buf); 155 156 if (!cwd) 157 abort(); 158 pstrcpy(pref_buf, sizeof(pref_buf), cwd); 159 pstrcat(pref_buf, pref_buf_len, "/"); 160 pstrcat(pref_buf, pref_buf_len, prefix); 161 free(cwd); 162 } else 163 pstrcpy(pref_buf, sizeof(pref_buf), prefix + 1); 164 165 base = new_entry("", NULL, pref_buf); 166 base = add_dir_maybe(base); 167 if (base->num_entries == 0) { 168 free (base); 169 base = NULL; 170 } else { 171 set_parents(base, base); 172 } 173 } 174 175 /* Look for path in emulation dir, otherwise return name. */ 176 const char *path(const char *name) 177 { 178 /* Only do absolute paths: quick and dirty, but should mostly be OK. 179 Could do relative by tracking cwd. */ 180 if (!base || !name || name[0] != '/') 181 return name; 182 183 return follow_path(base, name) ?: name; 184 } 185