1 /******************************************************************************
2  *
3  * hypfs.c
4  *
5  * Simple sysfs-like file system for the hypervisor.
6  */
7 
8 #include <xen/err.h>
9 #include <xen/guest_access.h>
10 #include <xen/hypercall.h>
11 #include <xen/hypfs.h>
12 #include <xen/lib.h>
13 #include <xen/param.h>
14 #include <xen/rwlock.h>
15 #include <public/hypfs.h>
16 
17 #ifdef CONFIG_COMPAT
18 #include <compat/hypfs.h>
19 CHECK_hypfs_dirlistentry;
20 #endif
21 
22 #define DIRENTRY_NAME_OFF offsetof(struct xen_hypfs_dirlistentry, name)
23 #define DIRENTRY_SIZE(name_len) \
24     (DIRENTRY_NAME_OFF +        \
25      ROUNDUP((name_len) + 1, alignof(struct xen_hypfs_direntry)))
26 
27 const struct hypfs_funcs hypfs_dir_funcs = {
28     .enter = hypfs_node_enter,
29     .exit = hypfs_node_exit,
30     .read = hypfs_read_dir,
31     .write = hypfs_write_deny,
32     .getsize = hypfs_getsize,
33     .findentry = hypfs_dir_findentry,
34 };
35 const struct hypfs_funcs hypfs_leaf_ro_funcs = {
36     .enter = hypfs_node_enter,
37     .exit = hypfs_node_exit,
38     .read = hypfs_read_leaf,
39     .write = hypfs_write_deny,
40     .getsize = hypfs_getsize,
41     .findentry = hypfs_leaf_findentry,
42 };
43 const struct hypfs_funcs hypfs_leaf_wr_funcs = {
44     .enter = hypfs_node_enter,
45     .exit = hypfs_node_exit,
46     .read = hypfs_read_leaf,
47     .write = hypfs_write_leaf,
48     .getsize = hypfs_getsize,
49     .findentry = hypfs_leaf_findentry,
50 };
51 const struct hypfs_funcs hypfs_bool_wr_funcs = {
52     .enter = hypfs_node_enter,
53     .exit = hypfs_node_exit,
54     .read = hypfs_read_leaf,
55     .write = hypfs_write_bool,
56     .getsize = hypfs_getsize,
57     .findentry = hypfs_leaf_findentry,
58 };
59 const struct hypfs_funcs hypfs_custom_wr_funcs = {
60     .enter = hypfs_node_enter,
61     .exit = hypfs_node_exit,
62     .read = hypfs_read_leaf,
63     .write = hypfs_write_custom,
64     .getsize = hypfs_getsize,
65     .findentry = hypfs_leaf_findentry,
66 };
67 
68 static DEFINE_RWLOCK(hypfs_lock);
69 enum hypfs_lock_state {
70     hypfs_unlocked,
71     hypfs_read_locked,
72     hypfs_write_locked
73 };
74 static DEFINE_PER_CPU(enum hypfs_lock_state, hypfs_locked);
75 static DEFINE_PER_CPU(void *, hypfs_dyndata);
76 
77 static DEFINE_PER_CPU(const struct hypfs_entry *, hypfs_last_node_entered);
78 
79 HYPFS_DIR_INIT(hypfs_root, "");
80 
hypfs_read_lock(void)81 static void hypfs_read_lock(void)
82 {
83     ASSERT(this_cpu(hypfs_locked) != hypfs_write_locked);
84 
85     read_lock(&hypfs_lock);
86     this_cpu(hypfs_locked) = hypfs_read_locked;
87 }
88 
hypfs_write_lock(void)89 static void hypfs_write_lock(void)
90 {
91     ASSERT(this_cpu(hypfs_locked) == hypfs_unlocked);
92 
93     write_lock(&hypfs_lock);
94     this_cpu(hypfs_locked) = hypfs_write_locked;
95 }
96 
hypfs_unlock(void)97 static void hypfs_unlock(void)
98 {
99     enum hypfs_lock_state locked = this_cpu(hypfs_locked);
100 
101     this_cpu(hypfs_locked) = hypfs_unlocked;
102 
103     switch ( locked )
104     {
105     case hypfs_read_locked:
106         read_unlock(&hypfs_lock);
107         break;
108     case hypfs_write_locked:
109         write_unlock(&hypfs_lock);
110         break;
111     default:
112         BUG();
113     }
114 }
115 
hypfs_node_enter(const struct hypfs_entry * entry)116 const struct hypfs_entry *cf_check hypfs_node_enter(
117     const struct hypfs_entry *entry)
118 {
119     return entry;
120 }
121 
hypfs_node_exit(const struct hypfs_entry * entry)122 void cf_check hypfs_node_exit(const struct hypfs_entry *entry)
123 {
124 }
125 
node_enter(const struct hypfs_entry * entry)126 static int node_enter(const struct hypfs_entry *entry)
127 {
128     const struct hypfs_entry **last = &this_cpu(hypfs_last_node_entered);
129 
130     entry = entry->funcs->enter(entry);
131     if ( IS_ERR(entry) )
132         return PTR_ERR(entry);
133 
134     ASSERT(entry);
135     ASSERT(!*last || *last == entry->parent);
136 
137     *last = entry;
138 
139     return 0;
140 }
141 
node_exit(const struct hypfs_entry * entry)142 static void node_exit(const struct hypfs_entry *entry)
143 {
144     const struct hypfs_entry **last = &this_cpu(hypfs_last_node_entered);
145 
146     ASSERT(*last == entry);
147     *last = entry->parent;
148 
149     entry->funcs->exit(entry);
150 }
151 
node_exit_all(void)152 static void node_exit_all(void)
153 {
154     const struct hypfs_entry **last = &this_cpu(hypfs_last_node_entered);
155 
156     while ( *last )
157         node_exit(*last);
158 }
159 
160 #undef hypfs_alloc_dyndata
hypfs_alloc_dyndata(unsigned long size)161 void *hypfs_alloc_dyndata(unsigned long size)
162 {
163     unsigned int cpu = smp_processor_id();
164     void **dyndata = &per_cpu(hypfs_dyndata, cpu);
165 
166     ASSERT(per_cpu(hypfs_locked, cpu) != hypfs_unlocked);
167     ASSERT(*dyndata == NULL);
168 
169     *dyndata = xzalloc_array(unsigned char, size);
170 
171     return *dyndata;
172 }
173 
hypfs_get_dyndata(void)174 void *hypfs_get_dyndata(void)
175 {
176     void *dyndata = this_cpu(hypfs_dyndata);
177 
178     ASSERT(dyndata);
179 
180     return dyndata;
181 }
182 
hypfs_free_dyndata(void)183 void hypfs_free_dyndata(void)
184 {
185     void **dyndata = &this_cpu(hypfs_dyndata);
186 
187     XFREE(*dyndata);
188 }
189 
add_entry(struct hypfs_entry_dir * parent,struct hypfs_entry * new)190 static int add_entry(struct hypfs_entry_dir *parent, struct hypfs_entry *new)
191 {
192     int ret = -ENOENT;
193     struct hypfs_entry *e;
194 
195     ASSERT(new->funcs->enter);
196     ASSERT(new->funcs->exit);
197     ASSERT(new->funcs->read);
198     ASSERT(new->funcs->write);
199     ASSERT(new->funcs->getsize);
200     ASSERT(new->funcs->findentry);
201 
202     hypfs_write_lock();
203 
204     list_for_each_entry ( e, &parent->dirlist, list )
205     {
206         int cmp = strcmp(e->name, new->name);
207 
208         if ( cmp > 0 )
209         {
210             ret = 0;
211             list_add_tail(&new->list, &e->list);
212             break;
213         }
214         if ( cmp == 0 )
215         {
216             ret = -EEXIST;
217             break;
218         }
219     }
220 
221     if ( ret == -ENOENT )
222     {
223         ret = 0;
224         list_add_tail(&new->list, &parent->dirlist);
225     }
226 
227     if ( !ret )
228     {
229         unsigned int sz = strlen(new->name);
230 
231         parent->e.size += DIRENTRY_SIZE(sz);
232         new->parent = &parent->e;
233     }
234 
235     hypfs_unlock();
236 
237     return ret;
238 }
239 
hypfs_add_dir(struct hypfs_entry_dir * parent,struct hypfs_entry_dir * dir,bool nofault)240 int hypfs_add_dir(struct hypfs_entry_dir *parent,
241                   struct hypfs_entry_dir *dir, bool nofault)
242 {
243     int ret;
244 
245     ret = add_entry(parent, &dir->e);
246     BUG_ON(nofault && ret);
247 
248     return ret;
249 }
250 
hypfs_add_dyndir(struct hypfs_entry_dir * parent,struct hypfs_entry_dir * template)251 void hypfs_add_dyndir(struct hypfs_entry_dir *parent,
252                       struct hypfs_entry_dir *template)
253 {
254     /*
255      * As the template is only a placeholder for possibly multiple dynamically
256      * generated directories, the link up to its parent can be static, while
257      * the "real" children of the parent are to be found via the parent's
258      * findentry function only.
259      */
260     template->e.parent = &parent->e;
261 }
262 
hypfs_add_leaf(struct hypfs_entry_dir * parent,struct hypfs_entry_leaf * leaf,bool nofault)263 int hypfs_add_leaf(struct hypfs_entry_dir *parent,
264                    struct hypfs_entry_leaf *leaf, bool nofault)
265 {
266     int ret;
267 
268     if ( !leaf->u.content )
269         ret = -EINVAL;
270     else
271         ret = add_entry(parent, &leaf->e);
272     BUG_ON(nofault && ret);
273 
274     return ret;
275 }
276 
hypfs_get_path_user(char * buf,XEN_GUEST_HANDLE_PARAM (const_char)uaddr,unsigned long ulen)277 static int hypfs_get_path_user(char *buf,
278                                XEN_GUEST_HANDLE_PARAM(const_char) uaddr,
279                                unsigned long ulen)
280 {
281     if ( ulen > XEN_HYPFS_MAX_PATHLEN )
282         return -EINVAL;
283 
284     if ( copy_from_guest(buf, uaddr, ulen) )
285         return -EFAULT;
286 
287     if ( memchr(buf, 0, ulen) != buf + ulen - 1 )
288         return -EINVAL;
289 
290     return 0;
291 }
292 
hypfs_leaf_findentry(const struct hypfs_entry_dir * dir,const char * name,unsigned int name_len)293 struct hypfs_entry *cf_check hypfs_leaf_findentry(
294     const struct hypfs_entry_dir *dir, const char *name, unsigned int name_len)
295 {
296     return ERR_PTR(-ENOTDIR);
297 }
298 
hypfs_dir_findentry(const struct hypfs_entry_dir * dir,const char * name,unsigned int name_len)299 struct hypfs_entry *cf_check hypfs_dir_findentry(
300     const struct hypfs_entry_dir *dir, const char *name, unsigned int name_len)
301 {
302     struct hypfs_entry *entry;
303 
304     list_for_each_entry ( entry, &dir->dirlist, list )
305     {
306         int cmp = strncmp(name, entry->name, name_len);
307 
308         if ( cmp < 0 )
309             return ERR_PTR(-ENOENT);
310 
311         if ( !cmp && strlen(entry->name) == name_len )
312             return entry;
313     }
314 
315     return ERR_PTR(-ENOENT);
316 }
317 
hypfs_get_entry_rel(struct hypfs_entry_dir * dir,const char * path)318 static struct hypfs_entry *hypfs_get_entry_rel(struct hypfs_entry_dir *dir,
319                                                const char *path)
320 {
321     const char *end;
322     struct hypfs_entry *entry;
323     unsigned int name_len;
324     int ret;
325 
326     for ( ; ; )
327     {
328         if ( dir->e.type != XEN_HYPFS_TYPE_DIR )
329             return ERR_PTR(-ENOENT);
330 
331         if ( !*path )
332             return &dir->e;
333 
334         end = strchr(path, '/');
335         if ( !end )
336             end = strchr(path, '\0');
337         name_len = end - path;
338 
339         ret = node_enter(&dir->e);
340         if ( ret )
341             return ERR_PTR(ret);
342 
343         entry = dir->e.funcs->findentry(dir, path, name_len);
344         if ( IS_ERR(entry) || !*end )
345             return entry;
346 
347         path = end + 1;
348         dir = container_of(entry, struct hypfs_entry_dir, e);
349     }
350 
351     return ERR_PTR(-ENOENT);
352 }
353 
hypfs_get_entry(const char * path)354 static struct hypfs_entry *hypfs_get_entry(const char *path)
355 {
356     if ( path[0] != '/' )
357         return ERR_PTR(-EINVAL);
358 
359     return hypfs_get_entry_rel(&hypfs_root, path + 1);
360 }
361 
hypfs_getsize(const struct hypfs_entry * entry)362 unsigned int cf_check hypfs_getsize(const struct hypfs_entry *entry)
363 {
364     return entry->size;
365 }
366 
367 /*
368  * Fill the direntry for a dynamically generated entry. Especially the
369  * generated name needs to be kept in sync with hypfs_gen_dyndir_id_entry().
370  */
hypfs_read_dyndir_id_entry(const struct hypfs_entry_dir * template,unsigned int id,bool is_last,XEN_GUEST_HANDLE_PARAM (void)* uaddr)371 int hypfs_read_dyndir_id_entry(const struct hypfs_entry_dir *template,
372                                unsigned int id, bool is_last,
373                                XEN_GUEST_HANDLE_PARAM(void) *uaddr)
374 {
375     struct xen_hypfs_dirlistentry direntry;
376     char name[HYPFS_DYNDIR_ID_NAMELEN];
377     unsigned int e_namelen, e_len;
378 
379     e_namelen = snprintf(name, sizeof(name), template->e.name, id);
380     if ( e_namelen >= sizeof(name) )
381     {
382         ASSERT_UNREACHABLE();
383         return -ENOBUFS;
384     }
385     e_len = DIRENTRY_SIZE(e_namelen);
386     direntry.e.pad = 0;
387     direntry.e.type = template->e.type;
388     direntry.e.encoding = template->e.encoding;
389     direntry.e.content_len = template->e.funcs->getsize(&template->e);
390     direntry.e.max_write_len = template->e.max_size;
391     direntry.off_next = is_last ? 0 : e_len;
392     if ( copy_to_guest(*uaddr, &direntry, 1) )
393         return -EFAULT;
394     if ( copy_to_guest_offset(*uaddr, DIRENTRY_NAME_OFF, name,
395                               e_namelen + 1) )
396         return -EFAULT;
397 
398     guest_handle_add_offset(*uaddr, e_len);
399 
400     return 0;
401 }
402 
hypfs_dyndir_enter(const struct hypfs_entry * entry)403 static const struct hypfs_entry *cf_check hypfs_dyndir_enter(
404     const struct hypfs_entry *entry)
405 {
406     const struct hypfs_dyndir_id *data;
407 
408     data = hypfs_get_dyndata();
409 
410     /* Use template with original enter function. */
411     return data->template->e.funcs->enter(&data->template->e);
412 }
413 
hypfs_dyndir_findentry(const struct hypfs_entry_dir * dir,const char * name,unsigned int name_len)414 static struct hypfs_entry *cf_check hypfs_dyndir_findentry(
415     const struct hypfs_entry_dir *dir, const char *name, unsigned int name_len)
416 {
417     const struct hypfs_dyndir_id *data;
418 
419     data = hypfs_get_dyndata();
420 
421     /* Use template with original findentry function. */
422     return data->template->e.funcs->findentry(data->template, name, name_len);
423 }
424 
hypfs_read_dyndir(const struct hypfs_entry * entry,XEN_GUEST_HANDLE_PARAM (void)uaddr)425 static int cf_check hypfs_read_dyndir(
426     const struct hypfs_entry *entry, XEN_GUEST_HANDLE_PARAM(void) uaddr)
427 {
428     const struct hypfs_dyndir_id *data;
429 
430     data = hypfs_get_dyndata();
431 
432     /* Use template with original read function. */
433     return data->template->e.funcs->read(&data->template->e, uaddr);
434 }
435 
436 /*
437  * Fill dyndata with a dynamically generated entry based on a template
438  * and a numerical id.
439  * Needs to be kept in sync with hypfs_read_dyndir_id_entry() regarding the
440  * name generated.
441  */
hypfs_gen_dyndir_id_entry(const struct hypfs_entry_dir * template,unsigned int id,void * data)442 struct hypfs_entry *hypfs_gen_dyndir_id_entry(
443     const struct hypfs_entry_dir *template, unsigned int id, void *data)
444 {
445     struct hypfs_dyndir_id *dyndata;
446 
447     dyndata = hypfs_get_dyndata();
448 
449     dyndata->template = template;
450     dyndata->id = id;
451     dyndata->data = data;
452     snprintf(dyndata->name, sizeof(dyndata->name), template->e.name, id);
453     dyndata->dir = *template;
454     dyndata->dir.e.name = dyndata->name;
455     dyndata->dir.e.funcs = &dyndata->funcs;
456     dyndata->funcs = *template->e.funcs;
457     dyndata->funcs.enter = hypfs_dyndir_enter;
458     dyndata->funcs.findentry = hypfs_dyndir_findentry;
459     dyndata->funcs.read = hypfs_read_dyndir;
460 
461     return &dyndata->dir.e;
462 }
463 
hypfs_dynid_entry_size(const struct hypfs_entry * template,unsigned int id)464 unsigned int hypfs_dynid_entry_size(const struct hypfs_entry *template,
465                                     unsigned int id)
466 {
467     return DIRENTRY_SIZE(snprintf(NULL, 0, template->name, id));
468 }
469 
hypfs_read_dir(const struct hypfs_entry * entry,XEN_GUEST_HANDLE_PARAM (void)uaddr)470 int cf_check hypfs_read_dir(const struct hypfs_entry *entry,
471                             XEN_GUEST_HANDLE_PARAM(void) uaddr)
472 {
473     const struct hypfs_entry_dir *d;
474     const struct hypfs_entry *e;
475     unsigned int size = entry->funcs->getsize(entry);
476     int ret;
477 
478     ASSERT(this_cpu(hypfs_locked) != hypfs_unlocked);
479 
480     d = container_of(entry, const struct hypfs_entry_dir, e);
481 
482     list_for_each_entry ( e, &d->dirlist, list )
483     {
484         struct xen_hypfs_dirlistentry direntry;
485         unsigned int e_namelen = strlen(e->name);
486         unsigned int e_len = DIRENTRY_SIZE(e_namelen);
487 
488         ret = node_enter(e);
489         if ( ret )
490             return ret;
491 
492         direntry.e.pad = 0;
493         direntry.e.type = e->type;
494         direntry.e.encoding = e->encoding;
495         direntry.e.content_len = e->funcs->getsize(e);
496         direntry.e.max_write_len = e->max_size;
497         direntry.off_next = list_is_last(&e->list, &d->dirlist) ? 0 : e_len;
498 
499         node_exit(e);
500 
501         if ( copy_to_guest(uaddr, &direntry, 1) )
502             return -EFAULT;
503 
504         if ( copy_to_guest_offset(uaddr, DIRENTRY_NAME_OFF,
505                                   e->name, e_namelen + 1) )
506             return -EFAULT;
507 
508         guest_handle_add_offset(uaddr, e_len);
509 
510         ASSERT(e_len <= size);
511         size -= e_len;
512     }
513 
514     return 0;
515 }
516 
hypfs_read_leaf(const struct hypfs_entry * entry,XEN_GUEST_HANDLE_PARAM (void)uaddr)517 int cf_check hypfs_read_leaf(
518     const struct hypfs_entry *entry, XEN_GUEST_HANDLE_PARAM(void) uaddr)
519 {
520     const struct hypfs_entry_leaf *l;
521     unsigned int size = entry->funcs->getsize(entry);
522 
523     ASSERT(this_cpu(hypfs_locked) != hypfs_unlocked);
524 
525     l = container_of(entry, const struct hypfs_entry_leaf, e);
526 
527     return copy_to_guest(uaddr, l->u.content, size) ?  -EFAULT : 0;
528 }
529 
hypfs_read(const struct hypfs_entry * entry,XEN_GUEST_HANDLE_PARAM (void)uaddr,unsigned long ulen)530 static int hypfs_read(const struct hypfs_entry *entry,
531                       XEN_GUEST_HANDLE_PARAM(void) uaddr, unsigned long ulen)
532 {
533     struct xen_hypfs_direntry e;
534     unsigned int size = entry->funcs->getsize(entry);
535     long ret = -EINVAL;
536 
537     if ( ulen < sizeof(e) )
538         goto out;
539 
540     e.pad = 0;
541     e.type = entry->type;
542     e.encoding = entry->encoding;
543     e.content_len = size;
544     e.max_write_len = entry->max_size;
545 
546     ret = -EFAULT;
547     if ( copy_to_guest(uaddr, &e, 1) )
548         goto out;
549 
550     ret = -ENOBUFS;
551     if ( ulen < size + sizeof(e) )
552         goto out;
553 
554     guest_handle_add_offset(uaddr, sizeof(e));
555 
556     ret = entry->funcs->read(entry, uaddr);
557 
558  out:
559     return ret;
560 }
561 
hypfs_write_leaf(struct hypfs_entry_leaf * leaf,XEN_GUEST_HANDLE_PARAM (const_void)uaddr,unsigned int ulen)562 int cf_check hypfs_write_leaf(
563     struct hypfs_entry_leaf *leaf, XEN_GUEST_HANDLE_PARAM(const_void) uaddr,
564     unsigned int ulen)
565 {
566     char *buf;
567     int ret;
568     struct hypfs_entry *e = &leaf->e;
569 
570     ASSERT(this_cpu(hypfs_locked) == hypfs_write_locked);
571     ASSERT(leaf->e.max_size);
572 
573     if ( ulen > e->max_size )
574         return -ENOSPC;
575 
576     if ( e->type != XEN_HYPFS_TYPE_STRING &&
577          e->type != XEN_HYPFS_TYPE_BLOB && ulen != e->funcs->getsize(e) )
578         return -EDOM;
579 
580     buf = xmalloc_array(char, ulen);
581     if ( !buf )
582         return -ENOMEM;
583 
584     ret = -EFAULT;
585     if ( copy_from_guest(buf, uaddr, ulen) )
586         goto out;
587 
588     ret = -EINVAL;
589     if ( e->type == XEN_HYPFS_TYPE_STRING &&
590          e->encoding == XEN_HYPFS_ENC_PLAIN &&
591          memchr(buf, 0, ulen) != (buf + ulen - 1) )
592         goto out;
593 
594     ret = 0;
595     memcpy(leaf->u.write_ptr, buf, ulen);
596     e->size = ulen;
597 
598  out:
599     xfree(buf);
600     return ret;
601 }
602 
hypfs_write_bool(struct hypfs_entry_leaf * leaf,XEN_GUEST_HANDLE_PARAM (const_void)uaddr,unsigned int ulen)603 int cf_check hypfs_write_bool(
604     struct hypfs_entry_leaf *leaf, XEN_GUEST_HANDLE_PARAM(const_void) uaddr,
605     unsigned int ulen)
606 {
607     bool buf;
608 
609     ASSERT(this_cpu(hypfs_locked) == hypfs_write_locked);
610     ASSERT(leaf->e.type == XEN_HYPFS_TYPE_BOOL &&
611            leaf->e.funcs->getsize(&leaf->e) == sizeof(bool) &&
612            leaf->e.max_size == sizeof(bool) );
613 
614     if ( ulen != leaf->e.max_size )
615         return -EDOM;
616 
617     if ( copy_from_guest(&buf, uaddr, ulen) )
618         return -EFAULT;
619 
620     *(bool *)leaf->u.write_ptr = buf;
621 
622     return 0;
623 }
624 
hypfs_write_custom(struct hypfs_entry_leaf * leaf,XEN_GUEST_HANDLE_PARAM (const_void)uaddr,unsigned int ulen)625 int cf_check hypfs_write_custom(
626     struct hypfs_entry_leaf *leaf, XEN_GUEST_HANDLE_PARAM(const_void) uaddr,
627     unsigned int ulen)
628 {
629     struct param_hypfs *p;
630     char *buf;
631     int ret;
632 
633     ASSERT(this_cpu(hypfs_locked) == hypfs_write_locked);
634     ASSERT(leaf->e.max_size);
635 
636     /* Avoid oversized buffer allocation. */
637     if ( ulen > MAX_PARAM_SIZE )
638         return -ENOSPC;
639 
640     buf = xzalloc_array(char, ulen);
641     if ( !buf )
642         return -ENOMEM;
643 
644     ret = -EFAULT;
645     if ( copy_from_guest(buf, uaddr, ulen) )
646         goto out;
647 
648     ret = -EDOM;
649     if ( memchr(buf, 0, ulen) != (buf + ulen - 1) )
650         goto out;
651 
652     p = container_of(leaf, struct param_hypfs, hypfs);
653     ret = p->func(buf);
654 
655  out:
656     xfree(buf);
657     return ret;
658 }
659 
hypfs_write_deny(struct hypfs_entry_leaf * leaf,XEN_GUEST_HANDLE_PARAM (const_void)uaddr,unsigned int ulen)660 int cf_check hypfs_write_deny(
661     struct hypfs_entry_leaf *leaf, XEN_GUEST_HANDLE_PARAM(const_void) uaddr,
662     unsigned int ulen)
663 {
664     return -EACCES;
665 }
666 
hypfs_write(struct hypfs_entry * entry,XEN_GUEST_HANDLE_PARAM (const_void)uaddr,unsigned long ulen)667 static int hypfs_write(struct hypfs_entry *entry,
668                        XEN_GUEST_HANDLE_PARAM(const_void) uaddr,
669                        unsigned long ulen)
670 {
671     struct hypfs_entry_leaf *l;
672 
673     l = container_of(entry, struct hypfs_entry_leaf, e);
674 
675     return entry->funcs->write(l, uaddr, ulen);
676 }
677 
do_hypfs_op(unsigned int cmd,XEN_GUEST_HANDLE_PARAM (const_char)arg1,unsigned long arg2,XEN_GUEST_HANDLE_PARAM (void)arg3,unsigned long arg4)678 long do_hypfs_op(
679     unsigned int cmd, XEN_GUEST_HANDLE_PARAM(const_char) arg1,
680     unsigned long arg2, XEN_GUEST_HANDLE_PARAM(void) arg3, unsigned long arg4)
681 {
682     int ret;
683     struct hypfs_entry *entry;
684     static char path[XEN_HYPFS_MAX_PATHLEN];
685 
686     if ( xsm_hypfs_op(XSM_PRIV) )
687         return -EPERM;
688 
689     if ( cmd == XEN_HYPFS_OP_get_version )
690     {
691         if ( !guest_handle_is_null(arg1) || arg2 ||
692              !guest_handle_is_null(arg3) || arg4 )
693             return -EINVAL;
694 
695         return XEN_HYPFS_VERSION;
696     }
697 
698     if ( cmd == XEN_HYPFS_OP_write_contents )
699         hypfs_write_lock();
700     else
701         hypfs_read_lock();
702 
703     ret = hypfs_get_path_user(path, arg1, arg2);
704     if ( ret )
705         goto out;
706 
707     entry = hypfs_get_entry(path);
708     if ( IS_ERR(entry) )
709     {
710         ret = PTR_ERR(entry);
711         goto out;
712     }
713 
714     ret = node_enter(entry);
715     if ( ret )
716         goto out;
717 
718     switch ( cmd )
719     {
720     case XEN_HYPFS_OP_read:
721         ret = hypfs_read(entry, arg3, arg4);
722         break;
723 
724     case XEN_HYPFS_OP_write_contents:
725         ret = hypfs_write(entry, guest_handle_const_cast(arg3, void), arg4);
726         break;
727 
728     default:
729         ret = -EOPNOTSUPP;
730         break;
731     }
732 
733  out:
734     node_exit_all();
735 
736     hypfs_unlock();
737 
738     return ret;
739 }
740