]>
git.proxmox.com Git - qemu.git/blob - fsdev/virtfs-proxy-helper.c
204bb4a5404dd1c68751b0178d1b06b806d58ea6
2 * Helper for QEMU Proxy FS Driver
3 * Copyright IBM, Corp. 2011
6 * M. Mohan Kumar <mohan@in.ibm.com>
8 * This work is licensed under the terms of the GNU GPL, version 2. See
9 * the COPYING file in the top-level directory.
12 #include <sys/socket.h>
19 #include <sys/resource.h>
24 #include <sys/capability.h>
25 #include <sys/fsuid.h>
30 #include "qemu-common.h"
31 #include "virtio-9p-marshal.h"
32 #include "hw/9pfs/virtio-9p-proxy.h"
33 #include "fsdev/virtio-9p-marshal.h"
35 #define PROGNAME "virtfs-proxy-helper"
37 static struct option helper_opts
[] = {
38 {"fd", required_argument
, NULL
, 'f'},
39 {"path", required_argument
, NULL
, 'p'},
40 {"nodaemon", no_argument
, NULL
, 'n'},
43 static bool is_daemon
;
45 static void do_log(int loglevel
, const char *format
, ...)
51 vsyslog(LOG_CRIT
, format
, ap
);
53 vfprintf(stderr
, format
, ap
);
58 static void do_perror(const char *string
)
61 syslog(LOG_CRIT
, "%s:%s", string
, strerror(errno
));
63 fprintf(stderr
, "%s:%s\n", string
, strerror(errno
));
67 static int do_cap_set(cap_value_t
*cap_value
, int size
, int reset
)
72 * Start with an empty set and set permitted and effective
76 do_perror("cap_init");
79 if (cap_set_flag(caps
, CAP_PERMITTED
, size
, cap_value
, CAP_SET
) < 0) {
80 do_perror("cap_set_flag");
84 caps
= cap_get_proc();
86 do_perror("cap_get_proc");
90 if (cap_set_flag(caps
, CAP_EFFECTIVE
, size
, cap_value
, CAP_SET
) < 0) {
91 do_perror("cap_set_flag");
94 if (cap_set_proc(caps
) < 0) {
95 do_perror("cap_set_proc");
106 static int init_capabilities(void)
108 /* helper needs following capbabilities only */
109 cap_value_t cap_list
[] = {
118 return do_cap_set(cap_list
, ARRAY_SIZE(cap_list
), 1);
121 static int socket_read(int sockfd
, void *buff
, ssize_t size
)
123 ssize_t retval
, total
= 0;
126 retval
= read(sockfd
, buff
, size
);
131 if (errno
== EINTR
) {
143 static int socket_write(int sockfd
, void *buff
, ssize_t size
)
145 ssize_t retval
, total
= 0;
148 retval
= write(sockfd
, buff
, size
);
150 if (errno
== EINTR
) {
162 static int read_request(int sockfd
, struct iovec
*iovec
, ProxyHeader
*header
)
167 * read the request header.
170 retval
= socket_read(sockfd
, iovec
->iov_base
, PROXY_HDR_SZ
);
174 iovec
->iov_len
= PROXY_HDR_SZ
;
175 retval
= proxy_unmarshal(iovec
, 0, "dd", &header
->type
, &header
->size
);
180 * We can't process message.size > PROXY_MAX_IO_SZ.
181 * Treat it as fatal error
183 if (header
->size
> PROXY_MAX_IO_SZ
) {
186 retval
= socket_read(sockfd
, iovec
->iov_base
+ PROXY_HDR_SZ
, header
->size
);
190 iovec
->iov_len
+= header
->size
;
194 static int send_fd(int sockfd
, int fd
)
199 struct cmsghdr
*cmsg
;
200 union MsgControl msg_control
;
202 iov
.iov_base
= &data
;
203 iov
.iov_len
= sizeof(data
);
205 memset(&msg
, 0, sizeof(msg
));
208 /* No ancillary data on error */
210 /* fd is really negative errno if the request failed */
213 data
= V9FS_FD_VALID
;
214 msg
.msg_control
= &msg_control
;
215 msg
.msg_controllen
= sizeof(msg_control
);
217 cmsg
= &msg_control
.cmsg
;
218 cmsg
->cmsg_len
= CMSG_LEN(sizeof(fd
));
219 cmsg
->cmsg_level
= SOL_SOCKET
;
220 cmsg
->cmsg_type
= SCM_RIGHTS
;
221 memcpy(CMSG_DATA(cmsg
), &fd
, sizeof(fd
));
225 retval
= sendmsg(sockfd
, &msg
, 0);
226 } while (retval
< 0 && errno
== EINTR
);
236 static int send_status(int sockfd
, struct iovec
*iovec
, int status
)
239 int retval
, msg_size
;;
242 header
.type
= T_ERROR
;
244 header
.type
= T_SUCCESS
;
246 header
.size
= sizeof(status
);
248 * marshal the return status. We don't check error.
249 * because we are sure we have enough space for the status
251 msg_size
= proxy_marshal(iovec
, 0, "ddd", header
.type
,
252 header
.size
, status
);
253 retval
= socket_write(sockfd
, iovec
->iov_base
, msg_size
);
261 * from man 7 capabilities, section
262 * Effect of User ID Changes on Capabilities:
263 * 4. If the file system user ID is changed from 0 to nonzero (see setfsuid(2))
264 * then the following capabilities are cleared from the effective set:
265 * CAP_CHOWN, CAP_DAC_OVERRIDE, CAP_DAC_READ_SEARCH, CAP_FOWNER, CAP_FSETID,
266 * CAP_LINUX_IMMUTABLE (since Linux 2.2.30), CAP_MAC_OVERRIDE, and CAP_MKNOD
267 * (since Linux 2.2.30). If the file system UID is changed from nonzero to 0,
268 * then any of these capabilities that are enabled in the permitted set
269 * are enabled in the effective set.
271 static int setfsugid(int uid
, int gid
)
274 * We still need DAC_OVERRIDE because we don't change
275 * supplementary group ids, and hence may be subjected DAC rules
277 cap_value_t cap_list
[] = {
284 if (uid
!= 0 || gid
!= 0) {
285 return do_cap_set(cap_list
, ARRAY_SIZE(cap_list
), 0);
291 * send response in two parts
293 * 2) Response or error status
294 * This function should be called with marshaled response
295 * send_response constructs header part and error part only.
296 * send response sends {ProxyHeader,Response} if the request was success
297 * otherwise sends {ProxyHeader,error status}
299 static int send_response(int sock
, struct iovec
*iovec
, int size
)
305 * If response size exceeds available iovec->iov_len,
308 if (size
> PROXY_MAX_IO_SZ
) {
314 * In case of error we would not have got the error encoded
315 * already so encode the error here.
317 header
.type
= T_ERROR
;
318 header
.size
= sizeof(size
);
319 proxy_marshal(iovec
, PROXY_HDR_SZ
, "d", size
);
321 header
.type
= T_SUCCESS
;
324 proxy_marshal(iovec
, 0, "dd", header
.type
, header
.size
);
325 retval
= socket_write(sock
, iovec
->iov_base
, header
.size
+ PROXY_HDR_SZ
);
332 static void stat_to_prstat(ProxyStat
*pr_stat
, struct stat
*stat
)
334 memset(pr_stat
, 0, sizeof(*pr_stat
));
335 pr_stat
->st_dev
= stat
->st_dev
;
336 pr_stat
->st_ino
= stat
->st_ino
;
337 pr_stat
->st_nlink
= stat
->st_nlink
;
338 pr_stat
->st_mode
= stat
->st_mode
;
339 pr_stat
->st_uid
= stat
->st_uid
;
340 pr_stat
->st_gid
= stat
->st_gid
;
341 pr_stat
->st_rdev
= stat
->st_rdev
;
342 pr_stat
->st_size
= stat
->st_size
;
343 pr_stat
->st_blksize
= stat
->st_blksize
;
344 pr_stat
->st_blocks
= stat
->st_blocks
;
345 pr_stat
->st_atim_sec
= stat
->st_atim
.tv_sec
;
346 pr_stat
->st_atim_nsec
= stat
->st_atim
.tv_nsec
;
347 pr_stat
->st_mtim_sec
= stat
->st_mtim
.tv_sec
;
348 pr_stat
->st_mtim_nsec
= stat
->st_mtim
.tv_nsec
;
349 pr_stat
->st_ctim_sec
= stat
->st_ctim
.tv_sec
;
350 pr_stat
->st_ctim_nsec
= stat
->st_ctim
.tv_nsec
;
353 static void statfs_to_prstatfs(ProxyStatFS
*pr_stfs
, struct statfs
*stfs
)
355 memset(pr_stfs
, 0, sizeof(*pr_stfs
));
356 pr_stfs
->f_type
= stfs
->f_type
;
357 pr_stfs
->f_bsize
= stfs
->f_bsize
;
358 pr_stfs
->f_blocks
= stfs
->f_blocks
;
359 pr_stfs
->f_bfree
= stfs
->f_bfree
;
360 pr_stfs
->f_bavail
= stfs
->f_bavail
;
361 pr_stfs
->f_files
= stfs
->f_files
;
362 pr_stfs
->f_ffree
= stfs
->f_ffree
;
363 pr_stfs
->f_fsid
[0] = stfs
->f_fsid
.__val
[0];
364 pr_stfs
->f_fsid
[1] = stfs
->f_fsid
.__val
[1];
365 pr_stfs
->f_namelen
= stfs
->f_namelen
;
366 pr_stfs
->f_frsize
= stfs
->f_frsize
;
370 * Gets stat/statfs information and packs in out_iovec structure
371 * on success returns number of bytes packed in out_iovec struture
372 * otherwise returns -errno
374 static int do_stat(int type
, struct iovec
*iovec
, struct iovec
*out_iovec
)
381 struct statfs stfs_buf
;
383 v9fs_string_init(&path
);
384 retval
= proxy_unmarshal(iovec
, PROXY_HDR_SZ
, "s", &path
);
391 retval
= lstat(path
.data
, &st_buf
);
395 stat_to_prstat(&pr_stat
, &st_buf
);
396 retval
= proxy_marshal(out_iovec
, PROXY_HDR_SZ
,
397 "qqqdddqqqqqqqqqq", pr_stat
.st_dev
,
398 pr_stat
.st_ino
, pr_stat
.st_nlink
,
399 pr_stat
.st_mode
, pr_stat
.st_uid
,
400 pr_stat
.st_gid
, pr_stat
.st_rdev
,
401 pr_stat
.st_size
, pr_stat
.st_blksize
,
403 pr_stat
.st_atim_sec
, pr_stat
.st_atim_nsec
,
404 pr_stat
.st_mtim_sec
, pr_stat
.st_mtim_nsec
,
405 pr_stat
.st_ctim_sec
, pr_stat
.st_ctim_nsec
);
409 retval
= statfs(path
.data
, &stfs_buf
);
413 statfs_to_prstatfs(&pr_stfs
, &stfs_buf
);
414 retval
= proxy_marshal(out_iovec
, PROXY_HDR_SZ
,
415 "qqqqqqqqqqq", pr_stfs
.f_type
,
416 pr_stfs
.f_bsize
, pr_stfs
.f_blocks
,
417 pr_stfs
.f_bfree
, pr_stfs
.f_bavail
,
418 pr_stfs
.f_files
, pr_stfs
.f_ffree
,
419 pr_stfs
.f_fsid
[0], pr_stfs
.f_fsid
[1],
420 pr_stfs
.f_namelen
, pr_stfs
.f_frsize
);
424 v9fs_string_free(&path
);
428 static int do_readlink(struct iovec
*iovec
, struct iovec
*out_iovec
)
432 V9fsString target
, path
;
434 v9fs_string_init(&path
);
435 retval
= proxy_unmarshal(iovec
, PROXY_HDR_SZ
, "sd", &path
, &size
);
437 v9fs_string_free(&path
);
440 buffer
= g_malloc(size
);
441 v9fs_string_init(&target
);
442 retval
= readlink(path
.data
, buffer
, size
);
444 buffer
[retval
] = '\0';
445 v9fs_string_sprintf(&target
, "%s", buffer
);
446 retval
= proxy_marshal(out_iovec
, PROXY_HDR_SZ
, "s", &target
);
451 v9fs_string_free(&target
);
452 v9fs_string_free(&path
);
457 * create other filesystem objects and send 0 on success
458 * return -errno on error
460 static int do_create_others(int type
, struct iovec
*iovec
)
464 int offset
= PROXY_HDR_SZ
;
465 V9fsString oldpath
, path
;
466 int mode
, uid
, gid
, cur_uid
, cur_gid
;
468 v9fs_string_init(&path
);
469 v9fs_string_init(&oldpath
);
473 retval
= proxy_unmarshal(iovec
, offset
, "dd", &uid
, &gid
);
478 retval
= setfsugid(uid
, gid
);
485 retval
= proxy_unmarshal(iovec
, offset
, "sdq", &path
, &mode
, &rdev
);
489 retval
= mknod(path
.data
, mode
, rdev
);
492 retval
= proxy_unmarshal(iovec
, offset
, "sd", &path
, &mode
);
496 retval
= mkdir(path
.data
, mode
);
499 retval
= proxy_unmarshal(iovec
, offset
, "ss", &oldpath
, &path
);
503 retval
= symlink(oldpath
.data
, path
.data
);
511 v9fs_string_free(&path
);
512 v9fs_string_free(&oldpath
);
513 setfsugid(cur_uid
, cur_gid
);
518 * create a file and send fd on success
519 * return -errno on error
521 static int do_create(struct iovec
*iovec
)
525 int flags
, mode
, uid
, gid
, cur_uid
, cur_gid
;
527 v9fs_string_init(&path
);
528 ret
= proxy_unmarshal(iovec
, PROXY_HDR_SZ
, "sdddd",
529 &path
, &flags
, &mode
, &uid
, &gid
);
531 goto unmarshal_err_out
;
535 ret
= setfsugid(uid
, gid
);
538 * On failure reset back to the
544 ret
= open(path
.data
, flags
, mode
);
550 setfsugid(cur_uid
, cur_gid
);
552 v9fs_string_free(&path
);
557 * open a file and send fd on success
558 * return -errno on error
560 static int do_open(struct iovec
*iovec
)
565 v9fs_string_init(&path
);
566 ret
= proxy_unmarshal(iovec
, PROXY_HDR_SZ
, "sd", &path
, &flags
);
570 ret
= open(path
.data
, flags
);
575 v9fs_string_free(&path
);
579 static void usage(char *prog
)
581 fprintf(stderr
, "usage: %s\n"
582 " -p|--path <path> 9p path to export\n"
583 " {-f|--fd <socket-descriptor>} socket file descriptor to be used\n"
584 " [-n|--nodaemon] Run as a normal program\n",
588 static int process_reply(int sock
, int type
,
589 struct iovec
*out_iovec
, int retval
)
594 if (send_fd(sock
, retval
) < 0) {
608 if (send_status(sock
, out_iovec
, retval
) < 0) {
615 if (send_response(sock
, out_iovec
, retval
) < 0) {
626 static int process_requests(int sock
)
632 struct timespec spec
[2];
633 V9fsString oldpath
, path
;
634 struct iovec in_iovec
, out_iovec
;
636 in_iovec
.iov_base
= g_malloc(PROXY_MAX_IO_SZ
+ PROXY_HDR_SZ
);
637 in_iovec
.iov_len
= PROXY_MAX_IO_SZ
+ PROXY_HDR_SZ
;
638 out_iovec
.iov_base
= g_malloc(PROXY_MAX_IO_SZ
+ PROXY_HDR_SZ
);
639 out_iovec
.iov_len
= PROXY_MAX_IO_SZ
+ PROXY_HDR_SZ
;
643 * initialize the header type, so that we send
644 * response to proper request type.
647 retval
= read_request(sock
, &in_iovec
, &header
);
652 switch (header
.type
) {
654 retval
= do_open(&in_iovec
);
657 retval
= do_create(&in_iovec
);
662 retval
= do_create_others(header
.type
, &in_iovec
);
665 v9fs_string_init(&path
);
666 v9fs_string_init(&oldpath
);
667 retval
= proxy_unmarshal(&in_iovec
, PROXY_HDR_SZ
,
668 "ss", &oldpath
, &path
);
670 retval
= link(oldpath
.data
, path
.data
);
675 v9fs_string_free(&oldpath
);
676 v9fs_string_free(&path
);
680 retval
= do_stat(header
.type
, &in_iovec
, &out_iovec
);
683 retval
= do_readlink(&in_iovec
, &out_iovec
);
686 v9fs_string_init(&path
);
687 retval
= proxy_unmarshal(&in_iovec
, PROXY_HDR_SZ
,
690 retval
= chmod(path
.data
, mode
);
695 v9fs_string_free(&path
);
698 v9fs_string_init(&path
);
699 retval
= proxy_unmarshal(&in_iovec
, PROXY_HDR_SZ
, "sdd", &path
,
702 retval
= lchown(path
.data
, uid
, gid
);
707 v9fs_string_free(&path
);
710 v9fs_string_init(&path
);
711 retval
= proxy_unmarshal(&in_iovec
, PROXY_HDR_SZ
, "sq",
714 retval
= truncate(path
.data
, offset
);
719 v9fs_string_free(&path
);
722 v9fs_string_init(&path
);
723 retval
= proxy_unmarshal(&in_iovec
, PROXY_HDR_SZ
, "sqqqq", &path
,
724 &spec
[0].tv_sec
, &spec
[0].tv_nsec
,
725 &spec
[1].tv_sec
, &spec
[1].tv_nsec
);
727 retval
= qemu_utimens(path
.data
, spec
);
732 v9fs_string_free(&path
);
735 v9fs_string_init(&path
);
736 v9fs_string_init(&oldpath
);
737 retval
= proxy_unmarshal(&in_iovec
, PROXY_HDR_SZ
,
738 "ss", &oldpath
, &path
);
740 retval
= rename(oldpath
.data
, path
.data
);
745 v9fs_string_free(&oldpath
);
746 v9fs_string_free(&path
);
749 v9fs_string_init(&path
);
750 retval
= proxy_unmarshal(&in_iovec
, PROXY_HDR_SZ
, "s", &path
);
752 retval
= remove(path
.data
);
757 v9fs_string_free(&path
);
764 if (process_reply(sock
, header
.type
, &out_iovec
, retval
) < 0) {
769 g_free(in_iovec
.iov_base
);
770 g_free(out_iovec
.iov_base
);
774 int main(int argc
, char **argv
)
785 c
= getopt_long(argc
, argv
, "p:nh?f:", helper_opts
,
792 rpath
= strdup(optarg
);
808 /* Parameter validation */
809 if (sock
== -1 || rpath
== NULL
) {
810 fprintf(stderr
, "socket descriptor or path not specified\n");
815 if (lstat(rpath
, &stbuf
) < 0) {
816 fprintf(stderr
, "invalid path \"%s\" specified, %s\n",
817 rpath
, strerror(errno
));
821 if (!S_ISDIR(stbuf
.st_mode
)) {
822 fprintf(stderr
, "specified path \"%s\" is not directory\n", rpath
);
827 if (daemon(0, 0) < 0) {
828 fprintf(stderr
, "daemon call failed\n");
831 openlog(PROGNAME
, LOG_PID
, LOG_DAEMON
);
834 do_log(LOG_INFO
, "Started\n");
836 if (chdir("/") < 0) {
840 if (chroot(rpath
) < 0) {
846 if (init_capabilities() < 0) {
850 process_requests(sock
);
852 do_log(LOG_INFO
, "Done\n");