]> git.proxmox.com Git - wasi-libc.git/blame - libc-bottom-half/sources/chdir.c
Use bulk memory opcodes when possible
[wasi-libc.git] / libc-bottom-half / sources / chdir.c
CommitLineData
5b148b61
AC
1#include <errno.h>
2#include <fcntl.h>
3#include <limits.h>
4#include <stdlib.h>
5#include <string.h>
6#include <sys/stat.h>
7#include <unistd.h>
8#include <wasi/libc-find-relpath.h>
9#include <wasi/libc.h>
10
11#ifdef _REENTRANT
12#error "chdir doesn't yet support multiple threads"
13#endif
14
15extern char *__wasilibc_cwd;
16static int __wasilibc_cwd_mallocd = 0;
17
18int chdir(const char *path)
19{
20 static char *relative_buf = NULL;
21 static size_t relative_buf_len = 0;
22
23 // Find a preopen'd directory as well as a relative path we're anchored
24 // from which we're changing directories to.
25 const char *abs;
26 int parent_fd = __wasilibc_find_relpath_alloc(path, &abs, &relative_buf, &relative_buf_len, 1);
27 if (parent_fd == -1)
28 return -1;
29
30 // Make sure that this directory we're accessing is indeed a directory.
31 struct stat dirinfo;
32 int ret = fstatat(parent_fd, relative_buf, &dirinfo, 0);
33 if (ret == -1)
34 return -1;
35 if (!S_ISDIR(dirinfo.st_mode)) {
36 errno = ENOTDIR;
37 return -1;
38 }
39
40 // Create a string that looks like:
41 //
42 // __wasilibc_cwd = "/" + abs + "/" + relative_buf
43 //
44 // If `relative_buf` is equal to "." or `abs` is equal to the empty string,
45 // however, we skip that part and the middle slash.
809e2d36 46 size_t abs_len = strlen(abs);
5b148b61
AC
47 int copy_relative = strcmp(relative_buf, ".") != 0;
48 int mid = copy_relative && abs[0] != 0;
809e2d36 49 char *new_cwd = malloc(1 + abs_len + mid + (copy_relative ? strlen(relative_buf) : 0) + 1);
5b148b61
AC
50 if (new_cwd == NULL) {
51 errno = ENOMEM;
52 return -1;
53 }
54 new_cwd[0] = '/';
55 strcpy(new_cwd + 1, abs);
56 if (mid)
809e2d36 57 new_cwd[1 + abs_len] = '/';
5b148b61 58 if (copy_relative)
809e2d36 59 strcpy(new_cwd + 1 + abs_len + mid, relative_buf);
5b148b61
AC
60
61 // And set our new malloc'd buffer into the global cwd, freeing the
62 // previous one if necessary.
63 char *prev_cwd = __wasilibc_cwd;
64 __wasilibc_cwd = new_cwd;
65 if (__wasilibc_cwd_mallocd)
66 free(prev_cwd);
67 __wasilibc_cwd_mallocd = 1;
68 return 0;
69}
70
71static const char *make_absolute(const char *path) {
72 static char *make_absolute_buf = NULL;
73 static size_t make_absolute_len = 0;
74
75 // If this path is absolute, then we return it as-is.
76 if (path[0] == '/') {
77 return path;
78 }
79
80 // If the path is empty, or points to the current directory, then return
81 // the current directory.
82 if (path[0] == 0 || !strcmp(path, ".") || !strcmp(path, "./")) {
83 return __wasilibc_cwd;
84 }
85
86 // If the path starts with `./` then we won't be appending that to the cwd.
87 if (path[0] == '.' && path[1] == '/')
88 path += 2;
89
90 // Otherwise we'll take the current directory, add a `/`, and then add the
91 // input `path`. Note that this doesn't do any normalization (like removing
92 // `/./`).
93 size_t cwd_len = strlen(__wasilibc_cwd);
94 size_t path_len = strlen(path);
95 int need_slash = __wasilibc_cwd[cwd_len - 1] == '/' ? 0 : 1;
96 size_t alloc_len = cwd_len + path_len + 1 + need_slash;
97 if (alloc_len > make_absolute_len) {
0c65fc5e
GS
98 char *tmp = realloc(make_absolute_buf, alloc_len);
99 if (tmp == NULL)
5b148b61 100 return NULL;
0c65fc5e 101 make_absolute_buf = tmp;
5b148b61
AC
102 make_absolute_len = alloc_len;
103 }
104 strcpy(make_absolute_buf, __wasilibc_cwd);
105 if (need_slash)
106 strcpy(make_absolute_buf + cwd_len, "/");
107 strcpy(make_absolute_buf + cwd_len + need_slash, path);
108 return make_absolute_buf;
109}
110
111// Helper function defined only in this object file and weakly referenced from
112// `preopens.c` and `posix.c` This function isn't necessary unless `chdir` is
113// pulled in because all paths are otherwise absolute or relative to the root.
114int __wasilibc_find_relpath_alloc(
115 const char *path,
116 const char **abs_prefix,
117 char **relative_buf,
118 size_t *relative_buf_len,
119 int can_realloc
120) {
121 // First, make our path absolute taking the cwd into account.
122 const char *abspath = make_absolute(path);
123 if (abspath == NULL) {
124 errno = ENOMEM;
125 return -1;
126 }
127
128 // Next use our absolute path and split it. Find the preopened `fd` parent
129 // directory and set `abs_prefix`. Next up we'll be trying to fit `rel`
130 // into `relative_buf`.
131 const char *rel;
132 int fd = __wasilibc_find_abspath(abspath, abs_prefix, &rel);
133 if (fd == -1)
134 return -1;
135
136 size_t rel_len = strlen(rel);
137 if (*relative_buf_len < rel_len + 1) {
138 if (!can_realloc) {
139 errno = ERANGE;
140 return -1;
141 }
142 char *tmp = realloc(*relative_buf, rel_len + 1);
143 if (tmp == NULL) {
144 errno = ENOMEM;
145 return -1;
146 }
147 *relative_buf = tmp;
148 *relative_buf_len = rel_len + 1;
149 }
150 strcpy(*relative_buf, rel);
151 return fd;
152}