root / hw / 9pfs / virtio-9p-synth.c @ 8d43d7e5
History | View | Annotate | Download (15.4 kB)
1 |
/*
|
---|---|
2 |
* Virtio 9p synthetic file system support
|
3 |
*
|
4 |
* Copyright IBM, Corp. 2011
|
5 |
*
|
6 |
* Authors:
|
7 |
* Malahal Naineni <malahal@us.ibm.com>
|
8 |
* Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
9 |
*
|
10 |
* This work is licensed under the terms of the GNU GPL, version 2. See
|
11 |
* the COPYING file in the top-level directory.
|
12 |
*
|
13 |
*/
|
14 |
|
15 |
#include "hw/virtio.h" |
16 |
#include "virtio-9p.h" |
17 |
#include "virtio-9p-xattr.h" |
18 |
#include "fsdev/qemu-fsdev.h" |
19 |
#include "virtio-9p-synth.h" |
20 |
|
21 |
#include <sys/stat.h> |
22 |
|
23 |
/* Root node for synth file system */
|
24 |
V9fsSynthNode v9fs_synth_root = { |
25 |
.name = "/",
|
26 |
.actual_attr = { |
27 |
.mode = 0555 | S_IFDIR,
|
28 |
.nlink = 1,
|
29 |
}, |
30 |
.attr = &v9fs_synth_root.actual_attr, |
31 |
}; |
32 |
|
33 |
static QemuMutex v9fs_synth_mutex;
|
34 |
static int v9fs_synth_node_count; |
35 |
/* set to 1 when the synth fs is ready */
|
36 |
static int v9fs_synth_fs; |
37 |
|
38 |
static V9fsSynthNode *v9fs_add_dir_node(V9fsSynthNode *parent, int mode, |
39 |
const char *name, |
40 |
V9fsSynthNodeAttr *attr, int inode)
|
41 |
{ |
42 |
V9fsSynthNode *node; |
43 |
|
44 |
/* Add directory type and remove write bits */
|
45 |
mode = ((mode & 0777) | S_IFDIR) & ~(S_IWUSR | S_IWGRP | S_IWOTH);
|
46 |
node = g_malloc0(sizeof(V9fsSynthNode));
|
47 |
if (attr) {
|
48 |
/* We are adding .. or . entries */
|
49 |
node->attr = attr; |
50 |
node->attr->nlink++; |
51 |
} else {
|
52 |
node->attr = &node->actual_attr; |
53 |
node->attr->inode = inode; |
54 |
node->attr->nlink = 1;
|
55 |
/* We don't allow write to directories */
|
56 |
node->attr->mode = mode; |
57 |
node->attr->write = NULL;
|
58 |
node->attr->read = NULL;
|
59 |
} |
60 |
node->private = node; |
61 |
strncpy(node->name, name, sizeof(node->name));
|
62 |
QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling); |
63 |
return node;
|
64 |
} |
65 |
|
66 |
int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode, |
67 |
const char *name, V9fsSynthNode **result) |
68 |
{ |
69 |
int ret;
|
70 |
V9fsSynthNode *node, *tmp; |
71 |
|
72 |
if (!v9fs_synth_fs) {
|
73 |
return EAGAIN;
|
74 |
} |
75 |
if (!name || (strlen(name) >= NAME_MAX)) {
|
76 |
return EINVAL;
|
77 |
} |
78 |
if (!parent) {
|
79 |
parent = &v9fs_synth_root; |
80 |
} |
81 |
qemu_mutex_lock(&v9fs_synth_mutex); |
82 |
QLIST_FOREACH(tmp, &parent->child, sibling) { |
83 |
if (!strcmp(tmp->name, name)) {
|
84 |
ret = EEXIST; |
85 |
goto err_out;
|
86 |
} |
87 |
} |
88 |
/* Add the name */
|
89 |
node = v9fs_add_dir_node(parent, mode, name, NULL, v9fs_synth_node_count++);
|
90 |
v9fs_add_dir_node(node, parent->attr->mode, "..",
|
91 |
parent->attr, parent->attr->inode); |
92 |
v9fs_add_dir_node(node, node->attr->mode, ".",
|
93 |
node->attr, node->attr->inode); |
94 |
*result = node; |
95 |
ret = 0;
|
96 |
err_out:
|
97 |
qemu_mutex_unlock(&v9fs_synth_mutex); |
98 |
return ret;
|
99 |
} |
100 |
|
101 |
int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode, |
102 |
const char *name, v9fs_synth_read read, |
103 |
v9fs_synth_write write, void *arg)
|
104 |
{ |
105 |
int ret;
|
106 |
V9fsSynthNode *node, *tmp; |
107 |
|
108 |
if (!v9fs_synth_fs) {
|
109 |
return EAGAIN;
|
110 |
} |
111 |
if (!name || (strlen(name) >= NAME_MAX)) {
|
112 |
return EINVAL;
|
113 |
} |
114 |
if (!parent) {
|
115 |
parent = &v9fs_synth_root; |
116 |
} |
117 |
|
118 |
qemu_mutex_lock(&v9fs_synth_mutex); |
119 |
QLIST_FOREACH(tmp, &parent->child, sibling) { |
120 |
if (!strcmp(tmp->name, name)) {
|
121 |
ret = EEXIST; |
122 |
goto err_out;
|
123 |
} |
124 |
} |
125 |
/* Add file type and remove write bits */
|
126 |
mode = ((mode & 0777) | S_IFREG);
|
127 |
node = g_malloc0(sizeof(V9fsSynthNode));
|
128 |
node->attr = &node->actual_attr; |
129 |
node->attr->inode = v9fs_synth_node_count++; |
130 |
node->attr->nlink = 1;
|
131 |
node->attr->read = read; |
132 |
node->attr->write = write; |
133 |
node->attr->mode = mode; |
134 |
node->private = arg; |
135 |
strncpy(node->name, name, sizeof(node->name));
|
136 |
QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling); |
137 |
ret = 0;
|
138 |
err_out:
|
139 |
qemu_mutex_unlock(&v9fs_synth_mutex); |
140 |
return ret;
|
141 |
} |
142 |
|
143 |
static void v9fs_synth_fill_statbuf(V9fsSynthNode *node, struct stat *stbuf) |
144 |
{ |
145 |
stbuf->st_dev = 0;
|
146 |
stbuf->st_ino = node->attr->inode; |
147 |
stbuf->st_mode = node->attr->mode; |
148 |
stbuf->st_nlink = node->attr->nlink; |
149 |
stbuf->st_uid = 0;
|
150 |
stbuf->st_gid = 0;
|
151 |
stbuf->st_rdev = 0;
|
152 |
stbuf->st_size = 0;
|
153 |
stbuf->st_blksize = 0;
|
154 |
stbuf->st_blocks = 0;
|
155 |
stbuf->st_atime = 0;
|
156 |
stbuf->st_mtime = 0;
|
157 |
stbuf->st_ctime = 0;
|
158 |
} |
159 |
|
160 |
static int v9fs_synth_lstat(FsContext *fs_ctx, |
161 |
V9fsPath *fs_path, struct stat *stbuf)
|
162 |
{ |
163 |
V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data; |
164 |
|
165 |
v9fs_synth_fill_statbuf(node, stbuf); |
166 |
return 0; |
167 |
} |
168 |
|
169 |
static int v9fs_synth_fstat(FsContext *fs_ctx, int fid_type, |
170 |
V9fsFidOpenState *fs, struct stat *stbuf)
|
171 |
{ |
172 |
V9fsSynthOpenState *synth_open = fs->private; |
173 |
v9fs_synth_fill_statbuf(synth_open->node, stbuf); |
174 |
return 0; |
175 |
} |
176 |
|
177 |
static int v9fs_synth_opendir(FsContext *ctx, |
178 |
V9fsPath *fs_path, V9fsFidOpenState *fs) |
179 |
{ |
180 |
V9fsSynthOpenState *synth_open; |
181 |
V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data; |
182 |
|
183 |
synth_open = g_malloc(sizeof(*synth_open));
|
184 |
synth_open->node = node; |
185 |
node->open_count++; |
186 |
fs->private = synth_open; |
187 |
return 0; |
188 |
} |
189 |
|
190 |
static int v9fs_synth_closedir(FsContext *ctx, V9fsFidOpenState *fs) |
191 |
{ |
192 |
V9fsSynthOpenState *synth_open = fs->private; |
193 |
V9fsSynthNode *node = synth_open->node; |
194 |
|
195 |
node->open_count--; |
196 |
g_free(synth_open); |
197 |
fs->private = NULL;
|
198 |
return 0; |
199 |
} |
200 |
|
201 |
static off_t v9fs_synth_telldir(FsContext *ctx, V9fsFidOpenState *fs)
|
202 |
{ |
203 |
V9fsSynthOpenState *synth_open = fs->private; |
204 |
return synth_open->offset;
|
205 |
} |
206 |
|
207 |
static void v9fs_synth_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) |
208 |
{ |
209 |
V9fsSynthOpenState *synth_open = fs->private; |
210 |
synth_open->offset = off; |
211 |
} |
212 |
|
213 |
static void v9fs_synth_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) |
214 |
{ |
215 |
v9fs_synth_seekdir(ctx, fs, 0);
|
216 |
} |
217 |
|
218 |
static void v9fs_synth_direntry(V9fsSynthNode *node, |
219 |
struct dirent *entry, off_t off)
|
220 |
{ |
221 |
strcpy(entry->d_name, node->name); |
222 |
entry->d_ino = node->attr->inode; |
223 |
entry->d_off = off + 1;
|
224 |
} |
225 |
|
226 |
static int v9fs_synth_get_dentry(V9fsSynthNode *dir, struct dirent *entry, |
227 |
struct dirent **result, off_t off)
|
228 |
{ |
229 |
int i = 0; |
230 |
V9fsSynthNode *node; |
231 |
|
232 |
rcu_read_lock(); |
233 |
QLIST_FOREACH(node, &dir->child, sibling) { |
234 |
/* This is the off child of the directory */
|
235 |
if (i == off) {
|
236 |
break;
|
237 |
} |
238 |
i++; |
239 |
} |
240 |
rcu_read_unlock(); |
241 |
if (!node) {
|
242 |
/* end of directory */
|
243 |
*result = NULL;
|
244 |
return 0; |
245 |
} |
246 |
v9fs_synth_direntry(node, entry, off); |
247 |
*result = entry; |
248 |
return 0; |
249 |
} |
250 |
|
251 |
static int v9fs_synth_readdir_r(FsContext *ctx, V9fsFidOpenState *fs, |
252 |
struct dirent *entry, struct dirent **result) |
253 |
{ |
254 |
int ret;
|
255 |
V9fsSynthOpenState *synth_open = fs->private; |
256 |
V9fsSynthNode *node = synth_open->node; |
257 |
ret = v9fs_synth_get_dentry(node, entry, result, synth_open->offset); |
258 |
if (!ret && *result != NULL) { |
259 |
synth_open->offset++; |
260 |
} |
261 |
return ret;
|
262 |
} |
263 |
|
264 |
static int v9fs_synth_open(FsContext *ctx, V9fsPath *fs_path, |
265 |
int flags, V9fsFidOpenState *fs)
|
266 |
{ |
267 |
V9fsSynthOpenState *synth_open; |
268 |
V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data; |
269 |
|
270 |
synth_open = g_malloc(sizeof(*synth_open));
|
271 |
synth_open->node = node; |
272 |
node->open_count++; |
273 |
fs->private = synth_open; |
274 |
return 0; |
275 |
} |
276 |
|
277 |
static int v9fs_synth_open2(FsContext *fs_ctx, V9fsPath *dir_path, |
278 |
const char *name, int flags, |
279 |
FsCred *credp, V9fsFidOpenState *fs) |
280 |
{ |
281 |
errno = ENOSYS; |
282 |
return -1; |
283 |
} |
284 |
|
285 |
static int v9fs_synth_close(FsContext *ctx, V9fsFidOpenState *fs) |
286 |
{ |
287 |
V9fsSynthOpenState *synth_open = fs->private; |
288 |
V9fsSynthNode *node = synth_open->node; |
289 |
|
290 |
node->open_count--; |
291 |
g_free(synth_open); |
292 |
fs->private = NULL;
|
293 |
return 0; |
294 |
} |
295 |
|
296 |
static ssize_t v9fs_synth_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
|
297 |
const struct iovec *iov, |
298 |
int iovcnt, off_t offset)
|
299 |
{ |
300 |
int i, count = 0, wcount; |
301 |
V9fsSynthOpenState *synth_open = fs->private; |
302 |
V9fsSynthNode *node = synth_open->node; |
303 |
if (!node->attr->write) {
|
304 |
errno = EPERM; |
305 |
return -1; |
306 |
} |
307 |
for (i = 0; i < iovcnt; i++) { |
308 |
wcount = node->attr->write(iov[i].iov_base, iov[i].iov_len, |
309 |
offset, node->private); |
310 |
offset += wcount; |
311 |
count += wcount; |
312 |
/* If we wrote less than requested. we are done */
|
313 |
if (wcount < iov[i].iov_len) {
|
314 |
break;
|
315 |
} |
316 |
} |
317 |
return count;
|
318 |
} |
319 |
|
320 |
static ssize_t v9fs_synth_preadv(FsContext *ctx, V9fsFidOpenState *fs,
|
321 |
const struct iovec *iov, |
322 |
int iovcnt, off_t offset)
|
323 |
{ |
324 |
int i, count = 0, rcount; |
325 |
V9fsSynthOpenState *synth_open = fs->private; |
326 |
V9fsSynthNode *node = synth_open->node; |
327 |
if (!node->attr->read) {
|
328 |
errno = EPERM; |
329 |
return -1; |
330 |
} |
331 |
for (i = 0; i < iovcnt; i++) { |
332 |
rcount = node->attr->read(iov[i].iov_base, iov[i].iov_len, |
333 |
offset, node->private); |
334 |
offset += rcount; |
335 |
count += rcount; |
336 |
/* If we read less than requested. we are done */
|
337 |
if (rcount < iov[i].iov_len) {
|
338 |
break;
|
339 |
} |
340 |
} |
341 |
return count;
|
342 |
} |
343 |
|
344 |
static int v9fs_synth_truncate(FsContext *ctx, V9fsPath *path, off_t offset) |
345 |
{ |
346 |
errno = ENOSYS; |
347 |
return -1; |
348 |
} |
349 |
|
350 |
static int v9fs_synth_chmod(FsContext *fs_ctx, V9fsPath *path, FsCred *credp) |
351 |
{ |
352 |
errno = EPERM; |
353 |
return -1; |
354 |
} |
355 |
|
356 |
static int v9fs_synth_mknod(FsContext *fs_ctx, V9fsPath *path, |
357 |
const char *buf, FsCred *credp) |
358 |
{ |
359 |
errno = EPERM; |
360 |
return -1; |
361 |
} |
362 |
|
363 |
static int v9fs_synth_mkdir(FsContext *fs_ctx, V9fsPath *path, |
364 |
const char *buf, FsCred *credp) |
365 |
{ |
366 |
errno = EPERM; |
367 |
return -1; |
368 |
} |
369 |
|
370 |
static ssize_t v9fs_synth_readlink(FsContext *fs_ctx, V9fsPath *path,
|
371 |
char *buf, size_t bufsz)
|
372 |
{ |
373 |
errno = ENOSYS; |
374 |
return -1; |
375 |
} |
376 |
|
377 |
static int v9fs_synth_symlink(FsContext *fs_ctx, const char *oldpath, |
378 |
V9fsPath *newpath, const char *buf, FsCred *credp) |
379 |
{ |
380 |
errno = EPERM; |
381 |
return -1; |
382 |
} |
383 |
|
384 |
static int v9fs_synth_link(FsContext *fs_ctx, V9fsPath *oldpath, |
385 |
V9fsPath *newpath, const char *buf) |
386 |
{ |
387 |
errno = EPERM; |
388 |
return -1; |
389 |
} |
390 |
|
391 |
static int v9fs_synth_rename(FsContext *ctx, const char *oldpath, |
392 |
const char *newpath) |
393 |
{ |
394 |
errno = EPERM; |
395 |
return -1; |
396 |
} |
397 |
|
398 |
static int v9fs_synth_chown(FsContext *fs_ctx, V9fsPath *path, FsCred *credp) |
399 |
{ |
400 |
errno = EPERM; |
401 |
return -1; |
402 |
} |
403 |
|
404 |
static int v9fs_synth_utimensat(FsContext *fs_ctx, V9fsPath *path, |
405 |
const struct timespec *buf) |
406 |
{ |
407 |
errno = EPERM; |
408 |
return 0; |
409 |
} |
410 |
|
411 |
static int v9fs_synth_remove(FsContext *ctx, const char *path) |
412 |
{ |
413 |
errno = EPERM; |
414 |
return -1; |
415 |
} |
416 |
|
417 |
static int v9fs_synth_fsync(FsContext *ctx, int fid_type, |
418 |
V9fsFidOpenState *fs, int datasync)
|
419 |
{ |
420 |
errno = ENOSYS; |
421 |
return 0; |
422 |
} |
423 |
|
424 |
static int v9fs_synth_statfs(FsContext *s, V9fsPath *fs_path, |
425 |
struct statfs *stbuf)
|
426 |
{ |
427 |
stbuf->f_type = 0xABCD;
|
428 |
stbuf->f_bsize = 512;
|
429 |
stbuf->f_blocks = 0;
|
430 |
stbuf->f_files = v9fs_synth_node_count; |
431 |
stbuf->f_namelen = NAME_MAX; |
432 |
return 0; |
433 |
} |
434 |
|
435 |
static ssize_t v9fs_synth_lgetxattr(FsContext *ctx, V9fsPath *path,
|
436 |
const char *name, void *value, size_t size) |
437 |
{ |
438 |
errno = ENOTSUP; |
439 |
return -1; |
440 |
} |
441 |
|
442 |
static ssize_t v9fs_synth_llistxattr(FsContext *ctx, V9fsPath *path,
|
443 |
void *value, size_t size)
|
444 |
{ |
445 |
errno = ENOTSUP; |
446 |
return -1; |
447 |
} |
448 |
|
449 |
static int v9fs_synth_lsetxattr(FsContext *ctx, V9fsPath *path, |
450 |
const char *name, void *value, |
451 |
size_t size, int flags)
|
452 |
{ |
453 |
errno = ENOTSUP; |
454 |
return -1; |
455 |
} |
456 |
|
457 |
static int v9fs_synth_lremovexattr(FsContext *ctx, |
458 |
V9fsPath *path, const char *name) |
459 |
{ |
460 |
errno = ENOTSUP; |
461 |
return -1; |
462 |
} |
463 |
|
464 |
static int v9fs_synth_name_to_path(FsContext *ctx, V9fsPath *dir_path, |
465 |
const char *name, V9fsPath *target) |
466 |
{ |
467 |
V9fsSynthNode *node; |
468 |
V9fsSynthNode *dir_node; |
469 |
|
470 |
/* "." and ".." are not allowed */
|
471 |
if (!strcmp(name, ".") || !strcmp(name, "..")) { |
472 |
errno = EINVAL; |
473 |
return -1; |
474 |
|
475 |
} |
476 |
if (!dir_path) {
|
477 |
dir_node = &v9fs_synth_root; |
478 |
} else {
|
479 |
dir_node = *(V9fsSynthNode **)dir_path->data; |
480 |
} |
481 |
if (!strcmp(name, "/")) { |
482 |
node = dir_node; |
483 |
goto out;
|
484 |
} |
485 |
/* search for the name in the childern */
|
486 |
rcu_read_lock(); |
487 |
QLIST_FOREACH(node, &dir_node->child, sibling) { |
488 |
if (!strcmp(node->name, name)) {
|
489 |
break;
|
490 |
} |
491 |
} |
492 |
rcu_read_unlock(); |
493 |
|
494 |
if (!node) {
|
495 |
errno = ENOENT; |
496 |
return -1; |
497 |
} |
498 |
out:
|
499 |
/* Copy the node pointer to fid */
|
500 |
target->data = g_malloc(sizeof(void *)); |
501 |
memcpy(target->data, &node, sizeof(void *)); |
502 |
target->size = sizeof(void *); |
503 |
return 0; |
504 |
} |
505 |
|
506 |
static int v9fs_synth_renameat(FsContext *ctx, V9fsPath *olddir, |
507 |
const char *old_name, V9fsPath *newdir, |
508 |
const char *new_name) |
509 |
{ |
510 |
errno = EPERM; |
511 |
return -1; |
512 |
} |
513 |
|
514 |
static int v9fs_synth_unlinkat(FsContext *ctx, V9fsPath *dir, |
515 |
const char *name, int flags) |
516 |
{ |
517 |
errno = EPERM; |
518 |
return -1; |
519 |
} |
520 |
|
521 |
static int v9fs_synth_init(FsContext *ctx) |
522 |
{ |
523 |
QLIST_INIT(&v9fs_synth_root.child); |
524 |
qemu_mutex_init(&v9fs_synth_mutex); |
525 |
|
526 |
/* Add "." and ".." entries for root */
|
527 |
v9fs_add_dir_node(&v9fs_synth_root, v9fs_synth_root.attr->mode, |
528 |
"..", v9fs_synth_root.attr, v9fs_synth_root.attr->inode);
|
529 |
v9fs_add_dir_node(&v9fs_synth_root, v9fs_synth_root.attr->mode, |
530 |
".", v9fs_synth_root.attr, v9fs_synth_root.attr->inode);
|
531 |
|
532 |
/* Mark the subsystem is ready for use */
|
533 |
v9fs_synth_fs = 1;
|
534 |
return 0; |
535 |
} |
536 |
|
537 |
FileOperations synth_ops = { |
538 |
.init = v9fs_synth_init, |
539 |
.lstat = v9fs_synth_lstat, |
540 |
.readlink = v9fs_synth_readlink, |
541 |
.close = v9fs_synth_close, |
542 |
.closedir = v9fs_synth_closedir, |
543 |
.open = v9fs_synth_open, |
544 |
.opendir = v9fs_synth_opendir, |
545 |
.rewinddir = v9fs_synth_rewinddir, |
546 |
.telldir = v9fs_synth_telldir, |
547 |
.readdir_r = v9fs_synth_readdir_r, |
548 |
.seekdir = v9fs_synth_seekdir, |
549 |
.preadv = v9fs_synth_preadv, |
550 |
.pwritev = v9fs_synth_pwritev, |
551 |
.chmod = v9fs_synth_chmod, |
552 |
.mknod = v9fs_synth_mknod, |
553 |
.mkdir = v9fs_synth_mkdir, |
554 |
.fstat = v9fs_synth_fstat, |
555 |
.open2 = v9fs_synth_open2, |
556 |
.symlink = v9fs_synth_symlink, |
557 |
.link = v9fs_synth_link, |
558 |
.truncate = v9fs_synth_truncate, |
559 |
.rename = v9fs_synth_rename, |
560 |
.chown = v9fs_synth_chown, |
561 |
.utimensat = v9fs_synth_utimensat, |
562 |
.remove = v9fs_synth_remove, |
563 |
.fsync = v9fs_synth_fsync, |
564 |
.statfs = v9fs_synth_statfs, |
565 |
.lgetxattr = v9fs_synth_lgetxattr, |
566 |
.llistxattr = v9fs_synth_llistxattr, |
567 |
.lsetxattr = v9fs_synth_lsetxattr, |
568 |
.lremovexattr = v9fs_synth_lremovexattr, |
569 |
.name_to_path = v9fs_synth_name_to_path, |
570 |
.renameat = v9fs_synth_renameat, |
571 |
.unlinkat = v9fs_synth_unlinkat, |
572 |
}; |