1 /*
2 * Copyright (c) 2024, Google Inc. All rights reserved.
3 * Author: codycswong@google.com (Cody Wong)
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files
7 * (the "Software"), to deal in the Software without restriction,
8 * including without limitation the rights to use, copy, modify, merge,
9 * publish, distribute, sublicense, and/or sell copies of the Software,
10 * and to permit persons to whom the Software is furnished to do so,
11 * subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24 #include <dev/virtio/9p.h>
25
26 #include <stdio.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <lk/err.h>
31 #include <lk/trace.h>
32
33 #include "v9fs_priv.h"
34
35 #define LOCAL_TRACE 0
36
v9fs_open_dir(fscookie * cookie,const char * path,dircookie ** dcookie)37 status_t v9fs_open_dir(fscookie *cookie, const char *path, dircookie **dcookie)
38 {
39 v9fs_t *v9fs = (v9fs_t *)cookie;
40 char temppath[FS_MAX_PATH_LEN];
41 uint32_t flags;
42 int ret;
43
44 LTRACEF("v9fs (%p) path (%s) dcookie (%p)\n", v9fs, path, dcookie);
45
46 strlcpy(temppath, path, sizeof(temppath));
47
48 /* create the dir object */
49 v9fs_dir_t *dir = calloc(1, sizeof(v9fs_dir_t));
50
51 if (!dir) {
52 ret = ERR_NO_MEMORY;
53 goto err;
54 }
55
56 dir->fid.fid = get_unused_fid(v9fs);
57
58 virtio_9p_msg_t twalk = {
59 .msg_type = P9_TWALK,
60 .tag = P9_TAG_DEFAULT,
61 .msg.twalk = {
62 .fid = v9fs->root.fid, .newfid = dir->fid.fid
63 }
64 };
65 virtio_9p_msg_t rwalk = {};
66
67 path_to_wname(temppath, &twalk.msg.twalk.nwname, twalk.msg.twalk.wname);
68
69 LTRACEF("twalk.msg.twalk.nwname (%u)\n", twalk.msg.twalk.nwname);
70 for (int i = 0; i < twalk.msg.twalk.nwname; i++) {
71 LTRACEF("twalk.msg.twalk.wname[%d] (%s)\n", i, twalk.msg.twalk.wname[i]);
72
73 if (strlen(twalk.msg.twalk.wname[i]) == 0) {
74 twalk.msg.twalk.wname[i] = "/";
75 }
76 }
77
78 if ((ret = virtio_9p_rpc(v9fs->dev, &twalk, &rwalk)) != NO_ERROR)
79 goto err;
80
81 if (rwalk.msg_type != P9_RWALK ||
82 rwalk.msg.rwalk.nwqid != twalk.msg.twalk.nwname) {
83 ret = ERR_NOT_FOUND;
84 goto err;
85 }
86
87 // assume all dir are opened as readonly directory
88 flags = O_DIRECTORY | O_RDONLY;
89
90 virtio_9p_msg_t tlopen = {
91 .msg_type= P9_TLOPEN,
92 .tag = P9_TAG_DEFAULT,
93 .msg.tlopen = {
94 .fid = dir->fid.fid, .flags = flags,
95 }
96 };
97 virtio_9p_msg_t rlopen = {};
98
99 if ((ret = virtio_9p_rpc(v9fs->dev, &tlopen, &rlopen)) != NO_ERROR)
100 goto des_rwalk;
101
102 if (rlopen.msg_type != P9_RLOPEN) {
103 ret = ERR_ACCESS_DENIED;
104 goto des_rwalk;
105 }
106
107 if (rlopen.msg.rlopen.qid.type != P9_QTDIR) {
108 ret = ERR_NOT_DIR;
109 goto des_rwalk;
110 }
111
112 dir->v9fs = v9fs;
113 dir->fid.qid = rlopen.msg.rlopen.qid;
114 dir->fid.iounit = rlopen.msg.rlopen.iounit;
115 dir->offset = 0;
116 dir->head = 0;
117 dir->tail = 0;
118
119 *dcookie = (dircookie *)dir;
120 list_add_tail(&v9fs->dirs, &dir->node);
121
122 virtio_9p_msg_destroy(&rlopen);
123 virtio_9p_msg_destroy(&rwalk);
124
125 return NO_ERROR;
126
127 des_rwalk:
128 virtio_9p_msg_destroy(&rwalk);
129 put_fid(v9fs, dir->fid.fid);
130
131 err:
132 free(dir);
133 LTRACEF("failed: %d\n", ret);
134 return ret;
135 }
136
v9fs_mkdir(fscookie * cookie,const char * path)137 status_t v9fs_mkdir(fscookie *cookie, const char *path)
138 {
139 v9fs_t *v9fs = (v9fs_t *)cookie;
140 char temppath[FS_MAX_PATH_LEN];
141 char *dirname;
142 uint32_t dfid, mode;
143 int ret;
144
145 LTRACEF("v9fs (%p) path (%s)\n", v9fs, path);
146
147 strlcpy(temppath, path, sizeof(temppath));
148
149 dfid = get_unused_fid(v9fs);
150
151 virtio_9p_msg_t twalk = {
152 .msg_type = P9_TWALK,
153 .tag = P9_TAG_DEFAULT,
154 .msg.twalk = {
155 .fid = v9fs->root.fid, .newfid = dfid,
156 }
157 };
158 virtio_9p_msg_t rwalk = {};
159
160 // separate the directory and the dirname
161 dirname = strrchr(temppath, '/');
162 if (!dirname || dirname == temppath) { // create on the root dir
163 dirname = temppath;
164 twalk.msg.twalk.nwname = 0;
165 } else { // create on a dir
166 // parse the parent directory
167 *dirname++ = '\0';
168 path_to_wname(temppath, &twalk.msg.twalk.nwname, twalk.msg.twalk.wname);
169 }
170
171 // walk to the parent directory
172 if ((ret = virtio_9p_rpc(v9fs->dev, &twalk, &rwalk)) != NO_ERROR)
173 goto err;
174
175 if (rwalk.msg_type != P9_RWALK ||
176 rwalk.msg.rwalk.nwqid != twalk.msg.twalk.nwname) {
177 ret = ERR_NOT_DIR;
178 goto err;
179 }
180
181 // assume the dir is created as 040755
182 mode = S_IFDIR | S_IRWXU |
183 S_IRGRP | S_IWGRP |
184 S_IROTH | S_IWOTH;
185
186 virtio_9p_msg_t tmkdir = {
187 .msg_type= P9_TMKDIR,
188 .tag = P9_TAG_DEFAULT,
189 .msg.tmkdir = {
190 .dfid = dfid, .name = dirname, .mode = mode,
191 }
192 };
193 virtio_9p_msg_t rmkdir = {};
194
195 if ((ret = virtio_9p_rpc(v9fs->dev, &tmkdir, &rmkdir)) != NO_ERROR)
196 goto des_rwalk;
197
198 if (rmkdir.msg_type != P9_RMKDIR) {
199 ret = ERR_NOT_ALLOWED;
200 }
201
202 virtio_9p_msg_destroy(&rmkdir);
203
204 des_rwalk:
205 virtio_9p_msg_destroy(&rwalk);
206
207 err:
208 put_fid(v9fs, dfid);
209 return ret;
210 }
211
v9fs_read_dir(dircookie * dcookie,struct dirent * ent)212 status_t v9fs_read_dir(dircookie *dcookie, struct dirent *ent)
213 {
214 v9fs_dir_t *dir = (v9fs_dir_t *)dcookie;
215 p9_dirent_t p9_dent;
216 status_t ret = NO_ERROR;
217 ssize_t sread = 0;
218
219 LTRACEF("dir (%p) ent (%p)\n", dir, ent);
220
221 if (!dir && !ent)
222 return ERR_INVALID_ARGS;
223
224 while (true) {
225 // read directory entries from v9p to fill buffer
226 if (dir->head == dir->tail) {
227 virtio_9p_msg_t treaddir = {
228 .msg_type= P9_TREADDIR,
229 .tag = P9_TAG_DEFAULT,
230 .msg.treaddir = {
231 .fid = dir->fid.fid, .offset = dir->offset,
232 .count = PAGE_SIZE,
233 }
234 };
235 virtio_9p_msg_t rreaddir = {};
236
237 if ((ret = virtio_9p_rpc(dir->v9fs->dev, &treaddir, &rreaddir)) !=
238 NO_ERROR) {
239 return ret;
240 }
241
242 if (rreaddir.msg_type != P9_RREADDIR) {
243 virtio_9p_msg_destroy(&rreaddir);
244 return ERR_IO;
245 }
246
247 dir->head = 0;
248 dir->tail = MIN(rreaddir.msg.rreaddir.count, PAGE_SIZE);
249 memcpy(&dir->data, rreaddir.msg.rreaddir.data, dir->tail);
250 LTRACEF("head (%u) tail (%u) data (%p)\n", dir->head, dir->tail,
251 dir->data);
252
253 virtio_9p_msg_destroy(&rreaddir);
254
255 if (dir->tail == 0) {
256 // no more entries
257 return ERR_OUT_OF_RANGE;
258 }
259 }
260
261 // read the dirent from buffer
262 while (dir->head < dir->tail) {
263 sread = p9_dirent_read(dir->data + dir->head, dir->tail - dir->head,
264 &p9_dent);
265 if (sread < NO_ERROR) {
266 // invalid dirent
267 return sread;
268 }
269
270 LTRACEF(
271 "head (%u) tail (%u) sread (%ld) offset (%llu) name "
272 "(%s)\n",
273 dir->head, dir->tail, sread, p9_dent.offset, p9_dent.name);
274
275 dir->head += sread;
276 snprintf(ent->name, FS_MAX_FILE_LEN, "%s", p9_dent.name);
277 dir->offset = p9_dent.offset;
278
279 p9_dirent_destroy(&p9_dent);
280
281 return NO_ERROR;
282 }
283 }
284
285 ASSERT(false); // Impossible to get here
286 }
287
v9fs_close_dir(dircookie * dcookie)288 status_t v9fs_close_dir(dircookie *dcookie)
289 {
290 v9fs_dir_t *dir = (v9fs_dir_t *)dcookie;
291
292 LTRACEF("dir (%p)\n", dir);
293
294 if (!dir)
295 return NO_ERROR;
296
297 put_fid(dir->v9fs, dir->fid.fid);
298 list_delete(&dir->node);
299 free(dir);
300
301 return NO_ERROR;
302 }
303