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