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