@@ -13,51 +13,42 @@ static int __wasilibc_cwd_mallocd = 0;
1313
1414int chdir (const char * path )
1515{
16+ #ifdef _REENTRANT
17+ #error "chdir doesn't yet support multiple threads"
18+ #endif
1619 static char * relative_buf = NULL ;
1720 static size_t relative_buf_len = 0 ;
1821
1922 // Find a preopen'd directory as well as a relative path we're anchored
2023 // from which we're changing directories to.
2124 const char * abs ;
22- int parent_fd ;
23- while (1 ) {
24- parent_fd = __wasilibc_find_relpath (path , & abs , relative_buf , relative_buf_len );
25- if (parent_fd != -1 )
26- break ;
27- if (errno != ERANGE )
28- return -1 ;
29- size_t new_len = relative_buf_len == 0 ? 16 : 2 * relative_buf_len ;
30- relative_buf = realloc (relative_buf , new_len );
31- if (relative_buf == NULL ) {
32- errno = ENOMEM ;
33- return -1 ;
34- }
35- relative_buf_len = new_len ;
36- }
25+ int parent_fd = __wasilibc_find_relpath_alloc (path , & abs , & relative_buf , & relative_buf_len , 1 );
26+ if (parent_fd == -1 )
27+ return -1 ;
3728
3829 // Make sure that this directory we're accessing is indeed a directory.
3930 struct stat dirinfo ;
4031 int ret = fstatat (parent_fd , relative_buf , & dirinfo , 0 );
41- if (ret == -1 ) {
32+ if (ret == -1 )
4233 return -1 ;
43- }
4434 if (!S_ISDIR (dirinfo .st_mode )) {
4535 errno = ENOTDIR ;
4636 return -1 ;
4737 }
4838
4939 // Copy over our new absolute path into `__wasilibc_cwd`. Only copy over
5040 // the relative portion of the path if it's not equal to `.`
51- size_t len = strlen (abs );
41+ size_t len = strlen (abs ) + 1 ;
5242 int copy_relative = strcmp (relative_buf , "." ) != 0 ;
5343 char * new_cwd = malloc (len + (copy_relative ? strlen (relative_buf ) : 0 ));
5444 if (new_cwd == NULL ) {
5545 errno = ENOMEM ;
5646 return -1 ;
5747 }
58- strcpy (new_cwd , abs );
48+ new_cwd [0 ] = '/' ;
49+ strcpy (new_cwd + 1 , abs );
5950 if (copy_relative )
60- strcpy (new_cwd + strlen (abs ), relative_buf );
51+ strcpy (new_cwd + 1 + strlen (abs ), relative_buf );
6152
6253 // And set our new malloc'd buffer into the global cwd, freeing the
6354 // previous one if necessary.
@@ -68,3 +59,85 @@ int chdir(const char *path)
6859 __wasilibc_cwd_mallocd = 1 ;
6960 return 0 ;
7061}
62+
63+ static const char * make_absolute (const char * path ) {
64+ static char * make_absolute_buf = NULL ;
65+ static size_t make_absolute_len = 0 ;
66+
67+ // If this path is absolute, then we return it as-is.
68+ if (path [0 ] == '/' ) {
69+ return path ;
70+ }
71+
72+ // If the path is empty, or points to the current directory, then return
73+ // the current directory.
74+ if (path [0 ] == 0 || !strcmp (path , "." ) || !strcmp (path , "./" )) {
75+ return __wasilibc_cwd ;
76+ }
77+
78+ // If the path starts with `./` then we won't be appending that to the cwd.
79+ if (path [0 ] == '.' && path [1 ] == '/' )
80+ path += 2 ;
81+
82+ // Otherwise we'll take the current directory, add a `/`, and then add the
83+ // input `path`. Note that this doesn't do any normalization (like removing
84+ // `/./`).
85+ size_t cwd_len = strlen (__wasilibc_cwd );
86+ size_t path_len = strlen (path );
87+ int need_slash = __wasilibc_cwd [cwd_len - 1 ] == '/' ? 0 : 1 ;
88+ size_t alloc_len = cwd_len + path_len + 1 + need_slash ;
89+ if (alloc_len > make_absolute_len ) {
90+ make_absolute_buf = realloc (make_absolute_buf , alloc_len );
91+ if (make_absolute_buf == NULL )
92+ return NULL ;
93+ make_absolute_len = alloc_len ;
94+ }
95+ strcpy (make_absolute_buf , __wasilibc_cwd );
96+ if (need_slash )
97+ strcpy (make_absolute_buf + cwd_len , "/" );
98+ strcpy (make_absolute_buf + cwd_len + need_slash , path );
99+ return make_absolute_buf ;
100+ }
101+
102+ // Helper function defined only in this object file and weakly referenced from
103+ // `preopens.c` and `posix.c` This function isn't necessary unless `chdir` is
104+ // pulled in because all paths are otherwise absolute or relative to the root.
105+ int __wasilibc_find_relpath_alloc (
106+ const char * path ,
107+ const char * * abs_prefix ,
108+ char * * relative_buf ,
109+ size_t * relative_buf_len ,
110+ int can_realloc
111+ ) {
112+ // First, make our path absolute taking the cwd into account.
113+ const char * abspath = make_absolute (path );
114+ if (abspath == NULL ) {
115+ errno = ENOMEM ;
116+ return -1 ;
117+ }
118+
119+ // Next use our absolute path and split it. Find the preopened `fd` parent
120+ // directory and set `abs_prefix`. Next up we'll be trying to fit `rel`
121+ // into `relative_buf`.
122+ const char * rel ;
123+ int fd = __wasilibc_find_abspath (abspath , abs_prefix , & rel );
124+ if (fd == -1 )
125+ return -1 ;
126+
127+ size_t rel_len = strlen (rel );
128+ if (* relative_buf_len < rel_len + 1 ) {
129+ if (!can_realloc ) {
130+ errno = ERANGE ;
131+ return -1 ;
132+ }
133+ char * tmp = realloc (* relative_buf , rel_len + 1 );
134+ if (tmp == NULL ) {
135+ errno = ENOMEM ;
136+ return -1 ;
137+ }
138+ * relative_buf = tmp ;
139+ * relative_buf_len = rel_len + 1 ;
140+ }
141+ strcpy (* relative_buf , rel );
142+ return fd ;
143+ }
0 commit comments