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