1 | // SPDX-License-Identifier: GPL-3.0-or-later |
2 | // userspace filesystems |
3 | |
4 | #include "mos/filesystem/userfs/userfs.h" |
5 | |
6 | #include "mos/filesystem/dentry.h" |
7 | #include "mos/filesystem/vfs_types.h" |
8 | #include "mos/filesystem/vfs_utils.h" |
9 | #include "mos/misc/profiling.h" |
10 | #include "mos/syslog/printk.h" |
11 | #include "proto/filesystem.pb.h" |
12 | #include "proto/filesystem.services.h" |
13 | |
14 | #include <librpc/macro_magic.h> |
15 | #include <librpc/rpc.h> |
16 | #include <librpc/rpc_client.h> |
17 | #include <librpc/rpc_server.h> |
18 | #include <mos/filesystem/fs_types.h> |
19 | #include <mos/proto/fs_server.h> |
20 | #include <mos_stdio.h> |
21 | #include <mos_stdlib.h> |
22 | #include <mos_string.h> |
23 | #include <pb.h> |
24 | #include <pb_decode.h> |
25 | #include <pb_encode.h> |
26 | |
27 | MOS_RPC_USERFS_CLIENT(fs_client) |
28 | |
29 | static const inode_ops_t userfs_iops; |
30 | static const file_ops_t userfs_fops; |
31 | static const inode_cache_ops_t userfs_inode_cache_ops; |
32 | static const superblock_ops_t userfs_sb_ops; |
33 | |
34 | #define userfs_get(_fs, _fmt, ...) \ |
35 | statement_expr(userfs_t *, { \ |
36 | retval = container_of(_fs, userfs_t, fs); \ |
37 | userfs_ensure_connected(retval); \ |
38 | pr_dinfo2(userfs, "calling '%s' (rpc_server '%s'): " _fmt, _fs->name, retval->rpc_server_name __VA_OPT__(, __VA_ARGS__)); \ |
39 | }) |
40 | |
41 | inode_t *i_from_pbfull(const mosrpc_fs_inode_info *stat, superblock_t *sb, void *private) |
42 | { |
43 | // enum pb_file_type_t -> enum file_type_t is safe here because they have the same values |
44 | inode_t *i = inode_create(sb, ino: stat->ino, type: (file_type_t) stat->type); |
45 | i->created = stat->created; |
46 | i->modified = stat->modified; |
47 | i->accessed = stat->accessed; |
48 | i->size = stat->size; |
49 | i->uid = stat->uid; |
50 | i->gid = stat->gid; |
51 | i->perm = stat->perm; |
52 | i->nlinks = stat->nlinks; |
53 | i->suid = stat->suid; |
54 | i->sgid = stat->sgid; |
55 | i->sticky = stat->sticky; |
56 | i->private_data = private; |
57 | i->ops = &userfs_iops; |
58 | i->file_ops = &userfs_fops; |
59 | return i; |
60 | } |
61 | |
62 | mosrpc_fs_inode_info *i_to_pb_full(const inode_t *i, mosrpc_fs_inode_info *pbi) |
63 | { |
64 | pbi->ino = i->ino; |
65 | pbi->type = i->type; |
66 | pbi->created = i->created; |
67 | pbi->modified = i->modified; |
68 | pbi->accessed = i->accessed; |
69 | pbi->size = i->size; |
70 | pbi->uid = i->uid; |
71 | pbi->gid = i->gid; |
72 | pbi->perm = i->perm; |
73 | pbi->nlinks = i->nlinks; |
74 | pbi->suid = i->suid; |
75 | pbi->sgid = i->sgid; |
76 | pbi->sticky = i->sticky; |
77 | // pbi->private_data = (ptr_t) i->private; |
78 | return pbi; |
79 | } |
80 | |
81 | mosrpc_fs_inode_ref i_to_pb_ref(const inode_t *i) |
82 | { |
83 | mosrpc_fs_inode_ref ref = { .data = (ptr_t) i->private_data }; // for userfs, private_data is the inode reference used by the server |
84 | return ref; |
85 | } |
86 | |
87 | void userfs_ensure_connected(userfs_t *userfs) |
88 | { |
89 | if (likely(userfs->rpc_server)) |
90 | return; |
91 | |
92 | userfs->rpc_server = rpc_client_create(server_name: userfs->rpc_server_name); |
93 | if (unlikely(!userfs->rpc_server)) |
94 | { |
95 | pr_warn("userfs_ensure_connected: failed to connect to %s" , userfs->rpc_server_name); |
96 | return; |
97 | } |
98 | } |
99 | |
100 | static bool userfs_iop_hardlink(dentry_t *d, inode_t *i, dentry_t *new_d) |
101 | { |
102 | MOS_UNUSED(i); |
103 | MOS_UNUSED(new_d); |
104 | |
105 | userfs_t *userfs = userfs_get(d->superblock->fs, "hardlink: %s" , dentry_name(d)); |
106 | MOS_UNUSED(userfs); |
107 | |
108 | return false; |
109 | } |
110 | |
111 | static void userfs_iop_iterate_dir(dentry_t *dentry, vfs_listdir_state_t *state, dentry_iterator_op add_record) |
112 | { |
113 | userfs_t *userfs = userfs_get(dentry->superblock->fs, "iterate_dir: %s" , dentry_name(dentry)); |
114 | |
115 | mosrpc_fs_readdir_request req = { 0 }; |
116 | mosrpc_fs_readdir_response resp = { 0 }; |
117 | req.i_ref = i_to_pb_ref(i: dentry->inode); |
118 | |
119 | const pf_point_t ev = profile_enter(); |
120 | const int result = fs_client_readdir(server_stub: userfs->rpc_server, request: &req, response: &resp); |
121 | profile_leave(ev, "userfs.'%s'.readdir" , userfs->rpc_server_name); |
122 | |
123 | if (result != RPC_RESULT_OK) |
124 | { |
125 | pr_warn("userfs_iop_iterate_dir: failed to readdir %s: %d" , dentry_name(dentry), result); |
126 | goto bail_out; |
127 | } |
128 | |
129 | if (!resp.entries_count) |
130 | { |
131 | pr_dwarn(userfs, "userfs_iop_iterate_dir: failed to readdir %s: %s" , dentry_name(dentry), resp.result.error); |
132 | goto bail_out; |
133 | } |
134 | |
135 | for (size_t i = 0; i < resp.entries_count; i++) |
136 | { |
137 | const mosrpc_fs_pb_dirent *pbde = &resp.entries[i]; |
138 | MOS_ASSERT(pbde->name); |
139 | add_record(state, pbde->ino, pbde->name, strlen(str: pbde->name), (file_type_t) pbde->type); |
140 | } |
141 | |
142 | bail_out: |
143 | pb_release(mosrpc_fs_readdir_response_fields, dest_struct: &resp); |
144 | } |
145 | |
146 | static bool userfs_iop_lookup(inode_t *dir, dentry_t *dentry) |
147 | { |
148 | userfs_t *userfs = userfs_get(dir->superblock->fs, "lookup: %s" , dentry_name(dentry)); |
149 | |
150 | bool ret = false; |
151 | mosrpc_fs_lookup_request req = { 0 }; |
152 | req.i_ref = i_to_pb_ref(i: dir); |
153 | req.name = (char *) dentry_name(dentry); |
154 | |
155 | mosrpc_fs_lookup_response resp = { 0 }; |
156 | const pf_point_t ev = profile_enter(); |
157 | const int result = fs_client_lookup(server_stub: userfs->rpc_server, request: &req, response: &resp); |
158 | profile_leave(ev, "userfs.'%s'.lookup" , userfs->rpc_server_name); |
159 | |
160 | if (result != RPC_RESULT_OK) |
161 | { |
162 | pr_warn("userfs_iop_lookup: failed to lookup %s: %d" , dentry_name(dentry), result); |
163 | goto leave; |
164 | } |
165 | |
166 | if (!resp.result.success) |
167 | goto leave; // ENOENT is not a big deal |
168 | |
169 | inode_t *i = i_from_pbfull(stat: &resp.i_info, sb: dir->superblock, private: (void *) resp.i_ref.data); |
170 | dentry_attach(d: dentry, inode: i); |
171 | dentry->superblock = i->superblock = dir->superblock; |
172 | i->ops = &userfs_iops; |
173 | i->cache.ops = &userfs_inode_cache_ops; |
174 | i->file_ops = &userfs_fops; |
175 | ret = true; |
176 | |
177 | leave: |
178 | pb_release(mosrpc_fs_lookup_response_fields, dest_struct: &resp); |
179 | return ret; |
180 | } |
181 | |
182 | static bool userfs_iop_mkdir(inode_t *dir, dentry_t *dentry, file_perm_t perm) |
183 | { |
184 | MOS_UNUSED(dir); |
185 | MOS_UNUSED(dentry); |
186 | MOS_UNUSED(perm); |
187 | |
188 | userfs_t *userfs = userfs_get(dir->superblock->fs, "mkdir: %s" , dentry_name(dentry)); |
189 | |
190 | bool ret = false; |
191 | |
192 | mosrpc_fs_make_dir_request req = { .name = (char *) dentry_name(dentry), .perm = perm, .i_ref = i_to_pb_ref(i: dir) }; |
193 | mosrpc_fs_make_dir_response resp = { 0 }; |
194 | |
195 | const pf_point_t pp = profile_enter(); |
196 | const int result = fs_client_make_dir(server_stub: userfs->rpc_server, request: &req, response: &resp); |
197 | profile_leave(pp, "userfs.'%s'.make_dir" , userfs->rpc_server_name); |
198 | |
199 | if (result != RPC_RESULT_OK) |
200 | { |
201 | pr_warn("userfs_iop_mkdir: failed to mkdir %s: %d" , dentry_name(dentry), result); |
202 | goto bail_out; |
203 | } |
204 | |
205 | if (!resp.result.success) |
206 | { |
207 | pr_dwarn(userfs, "userfs_iop_mkdir: failed to mkdir %s: %s" , dentry_name(dentry), resp.result.error); |
208 | goto bail_out; |
209 | } |
210 | |
211 | inode_t *i = i_from_pbfull(stat: &resp.i_info, sb: dir->superblock, private: (void *) resp.i_ref.data); |
212 | dentry_attach(d: dentry, inode: i); |
213 | dentry->superblock = i->superblock = dir->superblock; |
214 | i->ops = &userfs_iops; |
215 | i->cache.ops = &userfs_inode_cache_ops; |
216 | i->file_ops = &userfs_fops; |
217 | ret = true; |
218 | |
219 | bail_out: |
220 | pb_release(mosrpc_fs_make_dir_response_fields, dest_struct: &resp); |
221 | return ret; |
222 | } |
223 | |
224 | static bool userfs_iop_mknode(inode_t *dir, dentry_t *dentry, file_type_t type, file_perm_t perm, dev_t dev) |
225 | { |
226 | MOS_UNUSED(dir); |
227 | MOS_UNUSED(dentry); |
228 | MOS_UNUSED(type); |
229 | MOS_UNUSED(perm); |
230 | MOS_UNUSED(dev); |
231 | |
232 | userfs_t *userfs = userfs_get(dir->superblock->fs, "mknode: %s" , dentry_name(dentry)); |
233 | MOS_UNUSED(userfs); |
234 | |
235 | return false; |
236 | } |
237 | |
238 | static bool userfs_iop_newfile(inode_t *dir, dentry_t *dentry, file_type_t type, file_perm_t perm) |
239 | { |
240 | userfs_t *userfs = userfs_get(dir->superblock->fs, "newfile: %s" , dentry_name(dentry)); |
241 | |
242 | bool ret = false; |
243 | mosrpc_fs_create_file_request req; |
244 | req.i_ref = i_to_pb_ref(i: dir); |
245 | req.name = (char *) dentry_name(dentry); |
246 | req.type = type; |
247 | req.perm = perm; |
248 | |
249 | mosrpc_fs_create_file_response resp = { 0 }; |
250 | |
251 | const pf_point_t pp = profile_enter(); |
252 | const int result = fs_client_create_file(server_stub: userfs->rpc_server, request: &req, response: &resp); |
253 | profile_leave(pp, "userfs.'%s'.create_file" , userfs->rpc_server_name); |
254 | |
255 | if (result != RPC_RESULT_OK) |
256 | { |
257 | pr_warn("userfs_iop_newfile: failed to create file %s: %d" , dentry_name(dentry), result); |
258 | goto bail_out; |
259 | } |
260 | |
261 | if (!resp.result.success) |
262 | { |
263 | pr_dwarn(userfs, "userfs_iop_newfile: failed to create file %s: %s" , dentry_name(dentry), resp.result.error); |
264 | goto bail_out; |
265 | } |
266 | |
267 | inode_t *i = i_from_pbfull(stat: &resp.i_info, sb: dir->superblock, private: (void *) resp.i_ref.data); |
268 | dentry_attach(d: dentry, inode: i); |
269 | dentry->superblock = i->superblock = dir->superblock; |
270 | i->ops = &userfs_iops; |
271 | i->cache.ops = &userfs_inode_cache_ops; |
272 | i->file_ops = &userfs_fops; |
273 | ret = true; |
274 | |
275 | bail_out: |
276 | pb_release(mosrpc_fs_create_file_response_fields, dest_struct: &resp); |
277 | return ret; |
278 | } |
279 | |
280 | static size_t userfs_iop_readlink(dentry_t *dentry, char *buffer, size_t buflen) |
281 | { |
282 | userfs_t *userfs = userfs_get(dentry->superblock->fs, "readlink: %s" , dentry_name(dentry)); |
283 | |
284 | mosrpc_fs_readlink_request req = { 0 }; |
285 | mosrpc_fs_readlink_response resp = { 0 }; |
286 | req.i_ref = i_to_pb_ref(i: dentry->inode); |
287 | |
288 | const pf_point_t pp = profile_enter(); |
289 | const int result = fs_client_readlink(server_stub: userfs->rpc_server, request: &req, response: &resp); |
290 | profile_leave(pp, "userfs.'%s'.readlink" , userfs->rpc_server_name); |
291 | |
292 | if (result != RPC_RESULT_OK) |
293 | { |
294 | pr_warn("userfs_iop_readlink: failed to readlink %s: %d" , dentry_name(dentry), result); |
295 | goto bail_out; |
296 | } |
297 | |
298 | if (!resp.result.success) |
299 | { |
300 | pr_dwarn(userfs, "userfs_iop_readlink: failed to readlink %s: %s" , dentry_name(dentry), resp.result.error); |
301 | goto bail_out; |
302 | } |
303 | |
304 | size_t len = strlen(str: resp.target); |
305 | if (len > buflen) |
306 | len = buflen; |
307 | |
308 | memcpy(dest: buffer, src: resp.target, n: len); |
309 | return len; |
310 | |
311 | bail_out: |
312 | pb_release(mosrpc_fs_readlink_response_fields, dest_struct: &resp); |
313 | return -EIO; |
314 | } |
315 | |
316 | static bool userfs_iop_rename(inode_t *old_dir, dentry_t *old_dentry, inode_t *new_dir, dentry_t *new_dentry) |
317 | { |
318 | MOS_UNUSED(old_dir); |
319 | MOS_UNUSED(old_dentry); |
320 | MOS_UNUSED(new_dir); |
321 | MOS_UNUSED(new_dentry); |
322 | |
323 | userfs_t *userfs = userfs_get(old_dir->superblock->fs, "rename: %s -> %s" , dentry_name(old_dentry), dentry_name(new_dentry)); |
324 | MOS_UNUSED(userfs); |
325 | |
326 | return false; |
327 | } |
328 | |
329 | static bool userfs_iop_rmdir(inode_t *dir, dentry_t *dentry) |
330 | { |
331 | MOS_UNUSED(dir); |
332 | MOS_UNUSED(dentry); |
333 | |
334 | userfs_t *userfs = userfs_get(dir->superblock->fs, "rmdir: %s" , dentry_name(dentry)); |
335 | MOS_UNUSED(userfs); |
336 | |
337 | return false; |
338 | } |
339 | |
340 | static bool userfs_iop_symlink(inode_t *dir, dentry_t *dentry, const char *symname) |
341 | { |
342 | MOS_UNUSED(dir); |
343 | MOS_UNUSED(dentry); |
344 | MOS_UNUSED(symname); |
345 | |
346 | userfs_t *userfs = userfs_get(dir->superblock->fs, "symlink: %s" , dentry_name(dentry)); |
347 | MOS_UNUSED(userfs); |
348 | |
349 | return false; |
350 | } |
351 | |
352 | static bool userfs_iop_unlink(inode_t *dir, dentry_t *dentry) |
353 | { |
354 | userfs_t *userfs = userfs_get(dir->superblock->fs, "unlink: %s" , dentry_name(dentry)); |
355 | |
356 | mosrpc_fs_unlink_request req; |
357 | mosrpc_fs_unlink_response resp = { 0 }; |
358 | |
359 | req.i_ref = i_to_pb_ref(i: dir); |
360 | req.dentry.inode_id = dentry->inode->ino; |
361 | req.dentry.name = (char *) dentry_name(dentry); |
362 | |
363 | const pf_point_t pp = profile_enter(); |
364 | const int result = fs_client_unlink(server_stub: userfs->rpc_server, request: &req, response: &resp); |
365 | profile_leave(pp, "userfs.'%s'.unlink" , userfs->rpc_server_name); |
366 | |
367 | if (result != RPC_RESULT_OK) |
368 | { |
369 | pr_warn("userfs_iop_unlink: failed to unlink %s: %d" , dentry_name(dentry), result); |
370 | goto bail_out; |
371 | } |
372 | |
373 | if (!resp.result.success) |
374 | { |
375 | pr_dwarn(userfs, "userfs_iop_unlink: failed to unlink %s: %s" , dentry_name(dentry), resp.result.error); |
376 | goto bail_out; |
377 | } |
378 | |
379 | return true; |
380 | |
381 | bail_out: |
382 | pb_release(mosrpc_fs_unlink_response_fields, dest_struct: &resp); |
383 | return false; |
384 | } |
385 | |
386 | static const inode_ops_t userfs_iops = { |
387 | .hardlink = userfs_iop_hardlink, |
388 | .iterate_dir = userfs_iop_iterate_dir, |
389 | .lookup = userfs_iop_lookup, |
390 | .mkdir = userfs_iop_mkdir, |
391 | .mknode = userfs_iop_mknode, |
392 | .newfile = userfs_iop_newfile, |
393 | .readlink = userfs_iop_readlink, |
394 | .rename = userfs_iop_rename, |
395 | .rmdir = userfs_iop_rmdir, |
396 | .symlink = userfs_iop_symlink, |
397 | .unlink = userfs_iop_unlink, |
398 | }; |
399 | |
400 | static bool userfs_fop_open(inode_t *inode, file_t *file, bool created) |
401 | { |
402 | MOS_UNUSED(inode); |
403 | MOS_UNUSED(file); |
404 | MOS_UNUSED(created); |
405 | |
406 | return true; |
407 | } |
408 | |
409 | static const file_ops_t userfs_fops = { |
410 | .open = userfs_fop_open, |
411 | .read = vfs_generic_read, |
412 | .write = vfs_generic_write, |
413 | .release = NULL, |
414 | .seek = NULL, |
415 | .mmap = NULL, |
416 | .munmap = NULL, |
417 | }; |
418 | |
419 | static phyframe_t *userfs_inode_cache_fill_cache(inode_cache_t *cache, off_t pgoff) |
420 | { |
421 | userfs_t *userfs = userfs_get(cache->owner->superblock->fs, "fill_cache" , ); |
422 | |
423 | mosrpc_fs_getpage_request req = { 0 }; |
424 | req.i_ref = i_to_pb_ref(i: cache->owner); |
425 | req.pgoff = pgoff; |
426 | |
427 | mosrpc_fs_getpage_response resp = { 0 }; |
428 | |
429 | const pf_point_t pp = profile_enter(); |
430 | const int result = fs_client_get_page(server_stub: userfs->rpc_server, request: &req, response: &resp); |
431 | profile_leave(pp, "userfs.'%s'.getpage" , userfs->rpc_server_name); |
432 | |
433 | if (result != RPC_RESULT_OK) |
434 | { |
435 | pr_warn("userfs_inode_cache_fill_cache: failed to getpage: %d" , result); |
436 | goto bail_out; |
437 | } |
438 | |
439 | if (!resp.result.success) |
440 | { |
441 | pr_dwarn(userfs, "userfs_inode_cache_fill_cache: failed to getpage: %s" , resp.result.error); |
442 | goto bail_out; |
443 | } |
444 | |
445 | // allocate a page |
446 | phyframe_t *page = pmm_ref_one(mm_get_free_page()); |
447 | if (!page) |
448 | { |
449 | pr_warn("userfs_inode_cache_fill_cache: failed to allocate page" ); |
450 | goto bail_out; |
451 | } |
452 | |
453 | // copy the data from the server |
454 | memcpy(dest: (void *) phyframe_va(page), src: resp.data->bytes, MIN(resp.data->size, MOS_PAGE_SIZE)); |
455 | return page; |
456 | |
457 | bail_out: |
458 | pb_release(mosrpc_fs_getpage_response_fields, dest_struct: &resp); |
459 | return ERR_PTR(error: -EIO); |
460 | } |
461 | |
462 | long userfs_inode_cache_flush_page(inode_cache_t *cache, off_t pgoff, phyframe_t *page) |
463 | { |
464 | userfs_t *userfs = userfs_get(cache->owner->superblock->fs, "flush_page" , ); |
465 | |
466 | long ret = 0; |
467 | mosrpc_fs_putpage_request req = { 0 }; |
468 | req.i_ref = i_to_pb_ref(i: cache->owner); |
469 | req.pgoff = pgoff; |
470 | req.data = kmalloc(PB_BYTES_ARRAY_T_ALLOCSIZE(MOS_PAGE_SIZE)); |
471 | req.data->size = MOS_PAGE_SIZE; |
472 | memcpy(dest: req.data->bytes, src: (void *) phyframe_va(page), MOS_PAGE_SIZE); |
473 | |
474 | mosrpc_fs_putpage_response resp = { 0 }; |
475 | |
476 | const pf_point_t pp = profile_enter(); |
477 | const int result = fs_client_put_page(server_stub: userfs->rpc_server, request: &req, response: &resp); |
478 | profile_leave(pp, "userfs.'%s'.putpage" , userfs->rpc_server_name); |
479 | |
480 | if (result != RPC_RESULT_OK) |
481 | { |
482 | pr_warn("userfs_inode_cache_flush_page: failed to putpage: %d" , result); |
483 | ret = -EIO; |
484 | goto bail_out; |
485 | } |
486 | |
487 | if (!resp.result.success) |
488 | { |
489 | pr_dwarn(userfs, "userfs_inode_cache_flush_page: failed to putpage: %s" , resp.result.error); |
490 | ret = -EIO; |
491 | goto bail_out; |
492 | } |
493 | |
494 | ret = 0; |
495 | |
496 | bail_out: |
497 | pb_release(mosrpc_fs_putpage_request_fields, dest_struct: &req); |
498 | pb_release(mosrpc_fs_putpage_response_fields, dest_struct: &resp); |
499 | return ret; |
500 | } |
501 | |
502 | static const inode_cache_ops_t userfs_inode_cache_ops = { |
503 | .fill_cache = userfs_inode_cache_fill_cache, |
504 | .page_write_begin = simple_page_write_begin, |
505 | .page_write_end = simple_page_write_end, |
506 | .flush_page = userfs_inode_cache_flush_page, |
507 | }; |
508 | |
509 | long userfs_sync_inode(inode_t *inode) |
510 | { |
511 | userfs_t *userfs = userfs_get(inode->superblock->fs, "sync_inode: %llu" , inode->ino); |
512 | |
513 | mosrpc_fs_sync_inode_request req = { 0 }; |
514 | req.i_ref = i_to_pb_ref(i: inode); |
515 | req.i_info = *i_to_pb_full(i: inode, pbi: &req.i_info); |
516 | |
517 | mosrpc_fs_sync_inode_response resp = { 0 }; |
518 | const pf_point_t pp = profile_enter(); |
519 | const int result = fs_client_sync_inode(server_stub: userfs->rpc_server, request: &req, response: &resp); |
520 | profile_leave(pp, "userfs.'%s'.sync_inode" , userfs->rpc_server_name); |
521 | |
522 | if (result != RPC_RESULT_OK) |
523 | { |
524 | pr_warn("userfs_sync_inode: failed to sync inode %llu: %d" , inode->ino, result); |
525 | return -EIO; |
526 | } |
527 | |
528 | if (!resp.result.success) |
529 | { |
530 | pr_dwarn(userfs, "userfs_sync_inode: failed to sync inode %llu: %s" , inode->ino, resp.result.error); |
531 | return -EIO; |
532 | } |
533 | |
534 | return 0; |
535 | } |
536 | |
537 | static const superblock_ops_t userfs_sb_ops = { |
538 | .drop_inode = NULL, |
539 | .sync_inode = userfs_sync_inode, |
540 | }; |
541 | |
542 | dentry_t *userfs_fsop_mount(filesystem_t *fs, const char *device, const char *options) |
543 | { |
544 | userfs_t *userfs = userfs_get(fs, "mount: %s" , fs->name); |
545 | |
546 | const mosrpc_fs_mount_request req = { |
547 | .fs_name = (char *) fs->name, |
548 | .device = (char *) device, |
549 | .options = (char *) options, |
550 | }; |
551 | |
552 | mosrpc_fs_mount_response resp = { 0 }; |
553 | |
554 | const pf_point_t pp = profile_enter(); |
555 | const int result = fs_client_mount(server_stub: userfs->rpc_server, request: &req, response: &resp); |
556 | profile_leave(pp, "userfs.'%s'.mount" , userfs->rpc_server_name); |
557 | |
558 | if (result != RPC_RESULT_OK) |
559 | { |
560 | pr_warn("userfs_fsop_mount: failed to mount %s: %d" , fs->name, result); |
561 | pb_release(mosrpc_fs_mount_response_fields, dest_struct: &resp); |
562 | return ERR_PTR(error: -EIO); |
563 | } |
564 | |
565 | if (!resp.result.success) |
566 | { |
567 | pr_warn("userfs_fsop_mount: failed to mount %s: %s" , fs->name, resp.result.error); |
568 | pb_release(mosrpc_fs_mount_response_fields, dest_struct: &resp); |
569 | return ERR_PTR(error: -EIO); |
570 | } |
571 | |
572 | superblock_t *sb = kmalloc(superblock_cache); |
573 | sb->ops = &userfs_sb_ops; |
574 | |
575 | inode_t *i = i_from_pbfull(stat: &resp.root_info, sb, private: (void *) resp.root_ref.data); |
576 | |
577 | sb->fs = fs; |
578 | sb->root = dentry_get_from_parent(sb, NULL, NULL); |
579 | sb->root->superblock = i->superblock = sb; |
580 | dentry_attach(d: sb->root, inode: i); |
581 | return sb->root; |
582 | } |
583 | |