]> git.proxmox.com Git - qemu.git/blame - fsdev/virtfs-proxy-helper.c
hw/9pfs: Create other filesystem objects
[qemu.git] / fsdev / virtfs-proxy-helper.c
CommitLineData
17bff52b
MK
1/*
2 * Helper for QEMU Proxy FS Driver
3 * Copyright IBM, Corp. 2011
4 *
5 * Authors:
6 * M. Mohan Kumar <mohan@in.ibm.com>
7 *
8 * This work is licensed under the terms of the GNU GPL, version 2. See
9 * the COPYING file in the top-level directory.
10 */
11#include <stdio.h>
daf0b9ac 12#include <sys/socket.h>
17bff52b
MK
13#include <string.h>
14#include <sys/un.h>
15#include <limits.h>
16#include <signal.h>
17#include <errno.h>
18#include <stdlib.h>
19#include <sys/resource.h>
20#include <sys/stat.h>
21#include <getopt.h>
22#include <unistd.h>
23#include <syslog.h>
24#include <sys/capability.h>
25#include <sys/fsuid.h>
26#include <stdarg.h>
27#include <stdbool.h>
28#include "qemu-common.h"
29#include "virtio-9p-marshal.h"
30#include "hw/9pfs/virtio-9p-proxy.h"
daf0b9ac 31#include "fsdev/virtio-9p-marshal.h"
17bff52b
MK
32
33#define PROGNAME "virtfs-proxy-helper"
34
35static struct option helper_opts[] = {
36 {"fd", required_argument, NULL, 'f'},
37 {"path", required_argument, NULL, 'p'},
38 {"nodaemon", no_argument, NULL, 'n'},
39};
40
41static bool is_daemon;
42
43static void do_log(int loglevel, const char *format, ...)
44{
45 va_list ap;
46
47 va_start(ap, format);
48 if (is_daemon) {
49 vsyslog(LOG_CRIT, format, ap);
50 } else {
51 vfprintf(stderr, format, ap);
52 }
53 va_end(ap);
54}
55
56static void do_perror(const char *string)
57{
58 if (is_daemon) {
59 syslog(LOG_CRIT, "%s:%s", string, strerror(errno));
60 } else {
61 fprintf(stderr, "%s:%s\n", string, strerror(errno));
62 }
63}
64
65static int do_cap_set(cap_value_t *cap_value, int size, int reset)
66{
67 cap_t caps;
68 if (reset) {
69 /*
70 * Start with an empty set and set permitted and effective
71 */
72 caps = cap_init();
73 if (caps == NULL) {
74 do_perror("cap_init");
75 return -1;
76 }
77 if (cap_set_flag(caps, CAP_PERMITTED, size, cap_value, CAP_SET) < 0) {
78 do_perror("cap_set_flag");
79 goto error;
80 }
81 } else {
82 caps = cap_get_proc();
83 if (!caps) {
84 do_perror("cap_get_proc");
85 return -1;
86 }
87 }
88 if (cap_set_flag(caps, CAP_EFFECTIVE, size, cap_value, CAP_SET) < 0) {
89 do_perror("cap_set_flag");
90 goto error;
91 }
92 if (cap_set_proc(caps) < 0) {
93 do_perror("cap_set_proc");
94 goto error;
95 }
96 cap_free(caps);
97 return 0;
98
99error:
100 cap_free(caps);
101 return -1;
102}
103
104static int init_capabilities(void)
105{
106 /* helper needs following capbabilities only */
107 cap_value_t cap_list[] = {
108 CAP_CHOWN,
109 CAP_DAC_OVERRIDE,
110 CAP_FOWNER,
111 CAP_FSETID,
112 CAP_SETGID,
113 CAP_MKNOD,
114 CAP_SETUID,
115 };
116 return do_cap_set(cap_list, ARRAY_SIZE(cap_list), 1);
117}
118
119static int socket_read(int sockfd, void *buff, ssize_t size)
120{
121 ssize_t retval, total = 0;
122
123 while (size) {
124 retval = read(sockfd, buff, size);
125 if (retval == 0) {
126 return -EIO;
127 }
128 if (retval < 0) {
129 if (errno == EINTR) {
130 continue;
131 }
132 return -errno;
133 }
134 size -= retval;
135 buff += retval;
136 total += retval;
137 }
138 return total;
139}
140
141static int socket_write(int sockfd, void *buff, ssize_t size)
142{
143 ssize_t retval, total = 0;
144
145 while (size) {
146 retval = write(sockfd, buff, size);
147 if (retval < 0) {
148 if (errno == EINTR) {
149 continue;
150 }
151 return -errno;
152 }
153 size -= retval;
154 buff += retval;
155 total += retval;
156 }
157 return total;
158}
159
160static int read_request(int sockfd, struct iovec *iovec, ProxyHeader *header)
161{
162 int retval;
163
164 /*
165 * read the request header.
166 */
167 iovec->iov_len = 0;
168 retval = socket_read(sockfd, iovec->iov_base, PROXY_HDR_SZ);
169 if (retval < 0) {
170 return retval;
171 }
172 iovec->iov_len = PROXY_HDR_SZ;
173 retval = proxy_unmarshal(iovec, 0, "dd", &header->type, &header->size);
174 if (retval < 0) {
175 return retval;
176 }
177 /*
178 * We can't process message.size > PROXY_MAX_IO_SZ.
179 * Treat it as fatal error
180 */
181 if (header->size > PROXY_MAX_IO_SZ) {
182 return -ENOBUFS;
183 }
184 retval = socket_read(sockfd, iovec->iov_base + PROXY_HDR_SZ, header->size);
185 if (retval < 0) {
186 return retval;
187 }
188 iovec->iov_len += header->size;
189 return 0;
190}
191
daf0b9ac
MK
192static int send_fd(int sockfd, int fd)
193{
194 struct msghdr msg;
195 struct iovec iov;
196 int retval, data;
197 struct cmsghdr *cmsg;
198 union MsgControl msg_control;
199
200 iov.iov_base = &data;
201 iov.iov_len = sizeof(data);
202
203 memset(&msg, 0, sizeof(msg));
204 msg.msg_iov = &iov;
205 msg.msg_iovlen = 1;
206 /* No ancillary data on error */
207 if (fd < 0) {
208 /* fd is really negative errno if the request failed */
209 data = fd;
210 } else {
211 data = V9FS_FD_VALID;
212 msg.msg_control = &msg_control;
213 msg.msg_controllen = sizeof(msg_control);
214
215 cmsg = &msg_control.cmsg;
216 cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
217 cmsg->cmsg_level = SOL_SOCKET;
218 cmsg->cmsg_type = SCM_RIGHTS;
219 memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
220 }
221
222 do {
223 retval = sendmsg(sockfd, &msg, 0);
224 } while (retval < 0 && errno == EINTR);
225 if (fd >= 0) {
226 close(fd);
227 }
228 if (retval < 0) {
229 return retval;
230 }
231 return 0;
232}
233
39f8c32c
MK
234static int send_status(int sockfd, struct iovec *iovec, int status)
235{
236 ProxyHeader header;
237 int retval, msg_size;;
238
239 if (status < 0) {
240 header.type = T_ERROR;
241 } else {
242 header.type = T_SUCCESS;
243 }
244 header.size = sizeof(status);
245 /*
246 * marshal the return status. We don't check error.
247 * because we are sure we have enough space for the status
248 */
249 msg_size = proxy_marshal(iovec, 0, "ddd", header.type,
250 header.size, status);
251 retval = socket_write(sockfd, iovec->iov_base, msg_size);
252 if (retval < 0) {
253 return retval;
254 }
255 return 0;
256}
257
daf0b9ac
MK
258/*
259 * from man 7 capabilities, section
260 * Effect of User ID Changes on Capabilities:
261 * 4. If the file system user ID is changed from 0 to nonzero (see setfsuid(2))
262 * then the following capabilities are cleared from the effective set:
263 * CAP_CHOWN, CAP_DAC_OVERRIDE, CAP_DAC_READ_SEARCH, CAP_FOWNER, CAP_FSETID,
264 * CAP_LINUX_IMMUTABLE (since Linux 2.2.30), CAP_MAC_OVERRIDE, and CAP_MKNOD
265 * (since Linux 2.2.30). If the file system UID is changed from nonzero to 0,
266 * then any of these capabilities that are enabled in the permitted set
267 * are enabled in the effective set.
268 */
269static int setfsugid(int uid, int gid)
270{
271 /*
272 * We still need DAC_OVERRIDE because we don't change
273 * supplementary group ids, and hence may be subjected DAC rules
274 */
275 cap_value_t cap_list[] = {
276 CAP_DAC_OVERRIDE,
277 };
278
279 setfsgid(gid);
280 setfsuid(uid);
281
282 if (uid != 0 || gid != 0) {
283 return do_cap_set(cap_list, ARRAY_SIZE(cap_list), 0);
284 }
285 return 0;
286}
287
39f8c32c
MK
288/*
289 * create other filesystem objects and send 0 on success
290 * return -errno on error
291 */
292static int do_create_others(int type, struct iovec *iovec)
293{
294 dev_t rdev;
295 int retval = 0;
296 int offset = PROXY_HDR_SZ;
297 V9fsString oldpath, path;
298 int mode, uid, gid, cur_uid, cur_gid;
299
300 v9fs_string_init(&path);
301 v9fs_string_init(&oldpath);
302 cur_uid = geteuid();
303 cur_gid = getegid();
304
305 retval = proxy_unmarshal(iovec, offset, "dd", &uid, &gid);
306 if (retval < 0) {
307 return retval;
308 }
309 offset += retval;
310 retval = setfsugid(uid, gid);
311 if (retval < 0) {
312 retval = -errno;
313 goto err_out;
314 }
315 switch (type) {
316 case T_MKNOD:
317 retval = proxy_unmarshal(iovec, offset, "sdq", &path, &mode, &rdev);
318 if (retval < 0) {
319 goto err_out;
320 }
321 retval = mknod(path.data, mode, rdev);
322 break;
323 case T_MKDIR:
324 retval = proxy_unmarshal(iovec, offset, "sd", &path, &mode);
325 if (retval < 0) {
326 goto err_out;
327 }
328 retval = mkdir(path.data, mode);
329 break;
330 case T_SYMLINK:
331 retval = proxy_unmarshal(iovec, offset, "ss", &oldpath, &path);
332 if (retval < 0) {
333 goto err_out;
334 }
335 retval = symlink(oldpath.data, path.data);
336 break;
337 }
338 if (retval < 0) {
339 retval = -errno;
340 }
341
342err_out:
343 v9fs_string_free(&path);
344 v9fs_string_free(&oldpath);
345 setfsugid(cur_uid, cur_gid);
346 return retval;
347}
348
daf0b9ac
MK
349/*
350 * create a file and send fd on success
351 * return -errno on error
352 */
353static int do_create(struct iovec *iovec)
354{
355 int ret;
356 V9fsString path;
357 int flags, mode, uid, gid, cur_uid, cur_gid;
358
359 v9fs_string_init(&path);
360 ret = proxy_unmarshal(iovec, PROXY_HDR_SZ, "sdddd",
361 &path, &flags, &mode, &uid, &gid);
362 if (ret < 0) {
363 goto unmarshal_err_out;
364 }
365 cur_uid = geteuid();
366 cur_gid = getegid();
367 ret = setfsugid(uid, gid);
368 if (ret < 0) {
369 /*
370 * On failure reset back to the
371 * old uid/gid
372 */
373 ret = -errno;
374 goto err_out;
375 }
376 ret = open(path.data, flags, mode);
377 if (ret < 0) {
378 ret = -errno;
379 }
380
381err_out:
382 setfsugid(cur_uid, cur_gid);
383unmarshal_err_out:
384 v9fs_string_free(&path);
385 return ret;
386}
387
388/*
389 * open a file and send fd on success
390 * return -errno on error
391 */
392static int do_open(struct iovec *iovec)
393{
394 int flags, ret;
395 V9fsString path;
396
397 v9fs_string_init(&path);
398 ret = proxy_unmarshal(iovec, PROXY_HDR_SZ, "sd", &path, &flags);
399 if (ret < 0) {
400 goto err_out;
401 }
402 ret = open(path.data, flags);
403 if (ret < 0) {
404 ret = -errno;
405 }
406err_out:
407 v9fs_string_free(&path);
408 return ret;
409}
410
17bff52b
MK
411static void usage(char *prog)
412{
413 fprintf(stderr, "usage: %s\n"
414 " -p|--path <path> 9p path to export\n"
415 " {-f|--fd <socket-descriptor>} socket file descriptor to be used\n"
416 " [-n|--nodaemon] Run as a normal program\n",
417 basename(prog));
418}
419
39f8c32c
MK
420static int process_reply(int sock, int type,
421 struct iovec *out_iovec, int retval)
daf0b9ac
MK
422{
423 switch (type) {
424 case T_OPEN:
425 case T_CREATE:
426 if (send_fd(sock, retval) < 0) {
427 return -1;
428 }
429 break;
39f8c32c
MK
430 case T_MKNOD:
431 case T_MKDIR:
432 case T_SYMLINK:
433 case T_LINK:
434 if (send_status(sock, out_iovec, retval) < 0) {
435 return -1;
436 }
437 break;
daf0b9ac
MK
438 default:
439 return -1;
440 break;
441 }
442 return 0;
443}
444
17bff52b
MK
445static int process_requests(int sock)
446{
daf0b9ac 447 int retval = 0;
17bff52b 448 ProxyHeader header;
39f8c32c
MK
449 V9fsString oldpath, path;
450 struct iovec in_iovec, out_iovec;
451
452 in_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ);
453 in_iovec.iov_len = PROXY_MAX_IO_SZ + PROXY_HDR_SZ;
454 out_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ);
455 out_iovec.iov_len = PROXY_MAX_IO_SZ + PROXY_HDR_SZ;
17bff52b 456
17bff52b 457 while (1) {
daf0b9ac
MK
458 /*
459 * initialize the header type, so that we send
460 * response to proper request type.
461 */
462 header.type = 0;
17bff52b
MK
463 retval = read_request(sock, &in_iovec, &header);
464 if (retval < 0) {
daf0b9ac
MK
465 goto err_out;
466 }
467
468 switch (header.type) {
469 case T_OPEN:
470 retval = do_open(&in_iovec);
471 break;
472 case T_CREATE:
473 retval = do_create(&in_iovec);
474 break;
39f8c32c
MK
475 case T_MKNOD:
476 case T_MKDIR:
477 case T_SYMLINK:
478 retval = do_create_others(header.type, &in_iovec);
479 break;
480 case T_LINK:
481 v9fs_string_init(&path);
482 v9fs_string_init(&oldpath);
483 retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ,
484 "ss", &oldpath, &path);
485 if (retval > 0) {
486 retval = link(oldpath.data, path.data);
487 if (retval < 0) {
488 retval = -errno;
489 }
490 }
491 v9fs_string_free(&oldpath);
492 v9fs_string_free(&path);
493 break;
daf0b9ac
MK
494 default:
495 goto err_out;
496 break;
497 }
498
39f8c32c 499 if (process_reply(sock, header.type, &out_iovec, retval) < 0) {
daf0b9ac 500 goto err_out;
17bff52b
MK
501 }
502 }
daf0b9ac 503err_out:
17bff52b 504 g_free(in_iovec.iov_base);
39f8c32c 505 g_free(out_iovec.iov_base);
17bff52b
MK
506 return -1;
507}
508
509int main(int argc, char **argv)
510{
511 int sock;
512 char *rpath = NULL;
513 struct stat stbuf;
514 int c, option_index;
515
516 is_daemon = true;
517 sock = -1;
518 while (1) {
519 option_index = 0;
520 c = getopt_long(argc, argv, "p:nh?f:", helper_opts,
521 &option_index);
522 if (c == -1) {
523 break;
524 }
525 switch (c) {
526 case 'p':
527 rpath = strdup(optarg);
528 break;
529 case 'n':
530 is_daemon = false;
531 break;
532 case 'f':
533 sock = atoi(optarg);
534 break;
535 case '?':
536 case 'h':
537 default:
538 usage(argv[0]);
539 exit(EXIT_FAILURE);
540 }
541 }
542
543 /* Parameter validation */
544 if (sock == -1 || rpath == NULL) {
545 fprintf(stderr, "socket descriptor or path not specified\n");
546 usage(argv[0]);
547 exit(EXIT_FAILURE);
548 }
549
550 if (lstat(rpath, &stbuf) < 0) {
551 fprintf(stderr, "invalid path \"%s\" specified, %s\n",
552 rpath, strerror(errno));
553 exit(EXIT_FAILURE);
554 }
555
556 if (!S_ISDIR(stbuf.st_mode)) {
557 fprintf(stderr, "specified path \"%s\" is not directory\n", rpath);
558 exit(EXIT_FAILURE);
559 }
560
561 if (is_daemon) {
562 if (daemon(0, 0) < 0) {
563 fprintf(stderr, "daemon call failed\n");
564 exit(EXIT_FAILURE);
565 }
566 openlog(PROGNAME, LOG_PID, LOG_DAEMON);
567 }
568
569 do_log(LOG_INFO, "Started\n");
570
571 if (chdir("/") < 0) {
572 do_perror("chdir");
573 goto error;
574 }
575 if (chroot(rpath) < 0) {
576 do_perror("chroot");
577 goto error;
578 }
579 umask(0);
580
581 if (init_capabilities() < 0) {
582 goto error;
583 }
584
585 process_requests(sock);
586error:
587 do_log(LOG_INFO, "Done\n");
588 closelog();
589 return 0;
590}