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