@@ -13,51 +13,42 @@ static int __wasilibc_cwd_mallocd = 0;
13
13
14
14
int chdir (const char * path )
15
15
{
16
+ #ifdef _REENTRANT
17
+ #error "chdir doesn't yet support multiple threads"
18
+ #endif
16
19
static char * relative_buf = NULL ;
17
20
static size_t relative_buf_len = 0 ;
18
21
19
22
// Find a preopen'd directory as well as a relative path we're anchored
20
23
// from which we're changing directories to.
21
24
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 ;
37
28
38
29
// Make sure that this directory we're accessing is indeed a directory.
39
30
struct stat dirinfo ;
40
31
int ret = fstatat (parent_fd , relative_buf , & dirinfo , 0 );
41
- if (ret == -1 ) {
32
+ if (ret == -1 )
42
33
return -1 ;
43
- }
44
34
if (!S_ISDIR (dirinfo .st_mode )) {
45
35
errno = ENOTDIR ;
46
36
return -1 ;
47
37
}
48
38
49
39
// Copy over our new absolute path into `__wasilibc_cwd`. Only copy over
50
40
// 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 ;
52
42
int copy_relative = strcmp (relative_buf , "." ) != 0 ;
53
43
char * new_cwd = malloc (len + (copy_relative ? strlen (relative_buf ) : 0 ));
54
44
if (new_cwd == NULL ) {
55
45
errno = ENOMEM ;
56
46
return -1 ;
57
47
}
58
- strcpy (new_cwd , abs );
48
+ new_cwd [0 ] = '/' ;
49
+ strcpy (new_cwd + 1 , abs );
59
50
if (copy_relative )
60
- strcpy (new_cwd + strlen (abs ), relative_buf );
51
+ strcpy (new_cwd + 1 + strlen (abs ), relative_buf );
61
52
62
53
// And set our new malloc'd buffer into the global cwd, freeing the
63
54
// previous one if necessary.
@@ -68,3 +59,85 @@ int chdir(const char *path)
68
59
__wasilibc_cwd_mallocd = 1 ;
69
60
return 0 ;
70
61
}
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