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 <stdlib.h>
27 #include <string.h>
28 #include <lk/trace.h>
29 #include <lk/init.h>
30 #include <lk/list.h>
31 #include <lk/err.h>
32 #include <lib/fs.h>
33 #include <kernel/mutex.h>
34 
35 #include "v9fs_priv.h"
36 
37 #define LOCAL_TRACE 0
38 
path_to_wname(char * path,uint16_t * nwname,const char * wname[P9_MAXWELEM])39 status_t path_to_wname(char *path, uint16_t *nwname,
40                        const char *wname[P9_MAXWELEM])
41 {
42     char *cptr, *ncptr;
43     *nwname = 0;
44 
45     if (path[0] == '\0')
46         return NO_ERROR;
47 
48     cptr = path[0] == '/' ? path + 1 : path;
49 
50     while ((ncptr = strchr(cptr, '/')) != NULL) {
51         *ncptr = '\0';
52         if (strlen(cptr) != 0)
53             wname[(*nwname)++] = cptr;
54         cptr = ncptr + 1;
55 
56         if (*nwname == P9_MAXWELEM)
57             return ERR_BAD_PATH;
58     }
59 
60     wname[(*nwname)++] = strlen(cptr) != 0? cptr : ".";
61 
62     return NO_ERROR;
63 }
64 
get_unused_fid(v9fs_t * v9fs)65 uint32_t get_unused_fid(v9fs_t *v9fs)
66 {
67     mutex_acquire(&v9fs->lock);
68     uint32_t fid = v9fs->unused_fid++;
69     mutex_release(&v9fs->lock);
70     return fid;
71 }
72 
put_fid(v9fs_t * v9fs,uint32_t fid)73 void put_fid(v9fs_t *v9fs, uint32_t fid)
74 {
75     virtio_9p_msg_t tclunk = {
76         .msg_type = P9_TCLUNK,
77         .tag = P9_TAG_DEFAULT,
78         .msg.tclunk = { .fid = fid, }
79     };
80     virtio_9p_msg_t rclunk = {};
81 
82     ASSERT(virtio_9p_rpc(v9fs->dev, &tclunk, &rclunk) == NO_ERROR);
83     ASSERT(rclunk.msg_type == P9_RCLUNK);
84 
85     virtio_9p_msg_destroy(&rclunk);
86 }
87 
v9fs_mount(bdev_t * dev,fscookie ** cookie)88 status_t v9fs_mount(bdev_t *dev, fscookie **cookie)
89 {
90     status_t ret;
91 
92     LTRACEF("bdev (%p) cookie (%p)\n", dev, cookie);
93 
94     if (!dev) {
95         return ERR_INVALID_ARGS;
96     }
97 
98     v9fs_t *v9fs = calloc(1, sizeof(v9fs_t));
99 
100     if (!v9fs)
101         return ERR_NO_MEMORY;
102 
103     // initialize v9fs structure
104     v9fs->dev = virtio_9p_bdev_to_virtio_device(dev);
105     v9fs->bdev = dev;
106     v9fs->unused_fid = 0;
107     list_initialize(&v9fs->files);
108     list_initialize(&v9fs->dirs);
109     mutex_init(&v9fs->lock);
110     v9fs->root.fid = get_unused_fid(v9fs);
111 
112     LTRACEF("v9fs->root.fid: %u\n", v9fs->root.fid);
113     // attach to the host
114     virtio_9p_msg_t tatt = {
115         .msg_type = P9_TATTACH,
116         .tag = P9_TAG_DEFAULT,
117         .msg.tattach = {
118             .fid = v9fs->root.fid,
119             .afid = P9_FID_NOFID,
120             .uname = "root",
121             .aname = V9P_MOUNT_ANAME,
122             .n_uname = P9_UNAME_NONUNAME,
123         }
124     };
125     virtio_9p_msg_t ratt = {};
126 
127     if ((ret = virtio_9p_rpc(v9fs->dev, &tatt, &ratt)) != NO_ERROR)
128         goto err;
129 
130     v9fs->root.qid = ratt.msg.rattach.qid;
131 
132     virtio_9p_msg_destroy(&ratt);
133 
134     *cookie = (fscookie *)v9fs;
135 
136     return NO_ERROR;
137 
138 err:
139     LTRACEF("mount 9p dev (%s) failed: %d\n", dev->name, ret);
140 
141     free(v9fs);
142     v9fs = NULL;
143     return ret;
144 }
145 
v9fs_unmount(fscookie * cookie)146 status_t v9fs_unmount(fscookie *cookie)
147 {
148     v9fs_t *v9fs = (v9fs_t *)cookie;
149 
150     LTRACEF("v9fs (%p)\n", v9fs);
151 
152     if (v9fs)
153         free(v9fs);
154 
155     return 0;
156 }
157 
158 static const struct fs_api v9fs_api = {
159     .format = NULL,
160     .fs_stat = NULL,
161 
162     .mount = v9fs_mount,
163     .unmount = v9fs_unmount,
164     .open = v9fs_open_file,
165     .create = v9fs_create_file,
166     .remove = NULL,
167     .truncate = NULL,
168     .stat = v9fs_stat_file,
169     .read = v9fs_read_file,
170     .write = v9fs_write_file,
171     .close = v9fs_close_file,
172 
173     .mkdir = v9fs_mkdir,
174     .opendir = v9fs_open_dir,
175     .readdir = v9fs_read_dir,
176     .closedir = v9fs_close_dir,
177 
178     .file_ioctl = NULL,
179 };
180 
181 STATIC_FS_IMPL(9p, &v9fs_api);
182