1 /* 2 * Copyright (c) 2013 The Chromium OS Authors. All rights reserved. 3 * Use of this source code is governed by a BSD-style license that can be 4 * found in the LICENSE file. 5 */ 6 7 #define _GNU_SOURCE /* for RTLD_NEXT in dlfcn.h */ 8 9 #include <glib.h> 10 11 #include <dlfcn.h> 12 #include <errno.h> 13 #include <fcntl.h> 14 #include <stdarg.h> 15 #include <stdio.h> 16 #include <stdlib.h> 17 #include <string.h> 18 #include <sys/stat.h> 19 #include <sys/types.h> 20 21 /* The purpose of this library is to override the open/creat syscalls to 22 * redirect these calls for selected devices. Adding the library file to 23 * LD_PRELOAD is the general way to accomplish this. The arbitrary file mapping 24 * is specified in the environment variable FILE_REDIRECTION_PRELOADS as 25 * follows: 26 * 27 * FILE_REDIRECTIONS_PRELOAD=<path1>=<target1>:<path2>=<target2> 28 * 29 * Here, <path1> etc are the absolute paths to files for which open/close should 30 * be intercepted. <target1> etc are the alternative files to which these calls 31 * should be redirected. 32 * 33 * - ':' is used to separate file mappings 34 * - The special character ':' in the paths should be escaped with '\' 35 * 36 * Example: 37 * export FILE_REDIRECTIONS_PRELOAD=/tmp/file1=/tmp/file2 38 * LD_PRELOAD=./libfakesyscalls.so ./write_to_tmp_file1 39 * 40 * where write_to_tmp_file1 is some executable that opens and writes to 41 * /tmp/file1. When the program exits, /tmp/file2 would have been created and 42 * written to, not /tmp/file1. 43 * 44 * cf: fakesyscalls-exercise.c 45 * 46 * Thread safety: This library is not thread-safe. If two threads 47 * simultaneously call open/creat for the first time, internal data-structures 48 * in the library can be corrupted. 49 * It is safe to have subsequent calls to open/creat be concurrent. 50 */ 51 52 #ifdef FAKE_SYSCALLS_DEBUG 53 static const char *k_tmp_logging_file_full_path = "/tmp/fake_syscalls.dbg"; 54 static FILE *debug_file; 55 56 #define fake_syscalls_debug_init() \ 57 debug_file = fopen (k_tmp_logging_file_full_path, "w") 58 59 #define fake_syscalls_debug(...) \ 60 do { \ 61 if (debug_file) { \ 62 fprintf (debug_file, __VA_ARGS__); \ 63 fprintf (debug_file, "\n"); \ 64 } \ 65 } while (0) 66 67 #define fake_syscalls_debug_finish() \ 68 do { \ 69 if (debug_file) { \ 70 fclose (debug_file); \ 71 debug_file = NULL; \ 72 } \ 73 } while (0) 74 75 #else /* FAKE_SYSCALLS_DEBUG */ 76 #define fake_syscalls_debug_init() 77 #define fake_syscalls_debug(...) 78 #define fake_syscalls_debug_finish() 79 #endif /* FAKE_SYSCALLS_DEBUG */ 80 81 static GHashTable *file_redirection_map; 82 83 static const char *k_env_file_redirections = "FILE_REDIRECTIONS_PRELOAD"; 84 static const char *k_func_open = "open"; 85 static const char *k_func_creat = "creat"; 86 87 void __attribute__ ((constructor)) 88 fake_syscalls_init (void) 89 { 90 fake_syscalls_debug_init (); 91 fake_syscalls_debug ("Initialized fakesyscalls library."); 92 } 93 94 void __attribute__ ((destructor)) 95 fake_syscalls_finish (void) 96 { 97 if (file_redirection_map) 98 g_hash_table_unref (file_redirection_map); 99 fake_syscalls_debug ("Quit fakesyscalls library."); 100 fake_syscalls_debug_finish (); 101 } 102 103 static void 104 abort_on_error (GError *error) { 105 if (!error) 106 return; 107 108 fake_syscalls_debug ("Aborting on error: |%s|", error->message); 109 g_error_free (error); 110 fake_syscalls_debug_finish (); 111 g_assert (0); 112 } 113 114 static void 115 setup_redirection_map (void) 116 { 117 const char *orig_env; 118 GRegex *entry_delimiter, *key_value_delimiter, *escaped_colon; 119 gchar *buf; 120 gchar **redirections; 121 gchar **redirections_iter; 122 GError *error = NULL; 123 124 file_redirection_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, 125 g_free); 126 127 orig_env = getenv (k_env_file_redirections); 128 if (orig_env == NULL) 129 orig_env = ""; 130 fake_syscalls_debug ("FILE_REDIRECTIONS_PRELOAD=|%s|", orig_env); 131 132 entry_delimiter = g_regex_new ("(?:([^\\\\]):)|(?:^:)", 0, 0, &error); 133 abort_on_error (error); 134 key_value_delimiter = g_regex_new ("=", 0, 0, &error); 135 abort_on_error (error); 136 escaped_colon = g_regex_new ("(?:[^\\\\]\\\\:)|(?:^\\\\:)", 0, 0, &error); 137 abort_on_error (error); 138 139 buf = g_regex_replace (entry_delimiter, orig_env, -1, 0, "\\1;", 0, &error); 140 abort_on_error (error); 141 redirections = g_strsplit (buf, ";", 0); 142 g_free (buf); 143 144 for (redirections_iter = redirections; 145 *redirections_iter; 146 ++redirections_iter) { 147 gchar **parts; 148 149 if (g_strcmp0 ("", *redirections_iter) == 0) 150 continue; 151 152 /* Any ':' in the map has to be escaped with a '\' to allow for ':' to act 153 * as delimiter. Clean away the '\'. 154 */ 155 buf = g_regex_replace_literal (escaped_colon, *redirections_iter, -1, 0, 156 ":", 0, &error); 157 abort_on_error (error); 158 parts = g_regex_split (key_value_delimiter, buf, 0); 159 g_free (buf); 160 161 if (g_strv_length (parts) != 2) { 162 fake_syscalls_debug ("Error parsing redirection: |%s|. Malformed map?", 163 *redirections_iter); 164 g_strfreev (parts); 165 continue; 166 } 167 if (strlen (parts[0]) == 0 || parts[0][0] != '/' || 168 strlen (parts[1]) == 0 || parts[1][0] != '/') { 169 fake_syscalls_debug ("Error parsing redirection: |%s|." 170 "Invalid absolute paths.", 171 *redirections_iter); 172 g_strfreev (parts); 173 continue; 174 } 175 176 fake_syscalls_debug ("Inserted redirection: |%s|->|%s|", 177 parts[0], parts[1]); 178 g_hash_table_insert (file_redirection_map, 179 g_strdup (parts[0]), g_strdup (parts[1])); 180 g_strfreev (parts); 181 } 182 183 g_regex_unref (entry_delimiter); 184 g_regex_unref (key_value_delimiter); 185 g_regex_unref (escaped_colon); 186 g_strfreev (redirections); 187 } 188 189 int 190 open (const char *pathname, int flags, ...) 191 { 192 static int(*realfunc)(const char *, int, ...); 193 const char *redirection; 194 va_list ap; 195 gboolean is_creat = FALSE; 196 mode_t mode = S_IRUSR; /* Make compiler happy. Remain restrictive. */ 197 198 if (file_redirection_map == NULL) 199 setup_redirection_map (); 200 201 redirection = (char *) g_hash_table_lookup (file_redirection_map, pathname); 202 if (redirection == NULL) 203 redirection = pathname; 204 205 if (realfunc == NULL) 206 realfunc = (int(*)(const char *, int, ...))dlsym (RTLD_NEXT, k_func_open); 207 208 is_creat = flags & O_CREAT; 209 210 if (is_creat) { 211 va_start (ap, flags); 212 mode = va_arg (ap, mode_t); 213 va_end (ap); 214 fake_syscalls_debug ( 215 "Redirect: open (%s, %d, %d) --> open (%s, %d, %d)", 216 pathname, flags, mode, redirection, flags, mode); 217 return realfunc (redirection, flags, mode); 218 } else { 219 fake_syscalls_debug ( 220 "Redirect: open (%s, %d) --> open (%s, %d)", 221 pathname, flags, redirection, flags); 222 return realfunc (redirection, flags); 223 } 224 } 225 226 int 227 creat (const char *pathname, mode_t mode) 228 { 229 static int(*realfunc)(const char *, mode_t); 230 const char *redirection; 231 232 if (file_redirection_map == NULL) 233 setup_redirection_map (); 234 235 redirection = (char *) g_hash_table_lookup (file_redirection_map, pathname); 236 if (redirection == NULL) 237 redirection = pathname; 238 fake_syscalls_debug ( 239 "Redirect: creat (%s, %d) --> creat (%s, %d)", 240 pathname, mode, redirection, mode); 241 242 if (realfunc == NULL) 243 realfunc = (int(*)(const char *, mode_t))dlsym (RTLD_NEXT, k_func_creat); 244 245 return realfunc (redirection, mode); 246 } 247