From: "M. Mohan Kumar" <mo...@in.ibm.com> Add option to use named socket for communicating between proxy helper and qemu proxy FS. Access to socket can be given by using command line options -u and -g. We can achive the same using a shell script over qemu and virtfs-proxy-helper using exec fd<><socket-path>, and then passing that fd as argument to qemu and virtfs-proxy-helper. Also having a server like virtfs-proxy-helper listening on a pathname without any authentication is little bit scary. So we have to decide whether this patch is really needed.
Signed-off-by: M. Mohan Kumar <mo...@in.ibm.com> Signed-off-by: Aneesh Kumar K.V <aneesh.ku...@linux.vnet.ibm.com> --- fsdev/file-op-9p.h | 2 + fsdev/virtfs-proxy-helper.c | 86 ++++++++++++++++++++++++++++++++++++++- fsdev/virtfs-proxy-helper.texi | 4 ++ hw/9pfs/virtio-9p-proxy.c | 52 +++++++++++++++++++++--- qemu-config.c | 7 +++ qemu-options.hx | 15 +++++-- vl.c | 6 ++- 7 files changed, 157 insertions(+), 15 deletions(-) diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h index 73de79f..4f76f9d 100644 --- a/fsdev/file-op-9p.h +++ b/fsdev/file-op-9p.h @@ -57,6 +57,8 @@ typedef struct extended_ops { */ #define V9FS_SM_NONE 0x00000010 #define V9FS_RDONLY 0x00000020 +#define V9FS_PROXY_SOCK_FD 0x00000040 +#define V9FS_PROXY_SOCK_NAME 0x00000080 #define V9FS_SEC_MASK 0x0000001C diff --git a/fsdev/virtfs-proxy-helper.c b/fsdev/virtfs-proxy-helper.c index 3beed25..baafee2 100644 --- a/fsdev/virtfs-proxy-helper.c +++ b/fsdev/virtfs-proxy-helper.c @@ -57,6 +57,9 @@ static struct option helper_opts[] = { {"fd", required_argument, NULL, 'f'}, {"path", required_argument, NULL, 'p'}, {"nodaemon", no_argument, NULL, 'n'}, + {"socket", required_argument, NULL, 's'}, + {"uid", required_argument, NULL, 'u'}, + {"gid", required_argument, NULL, 'g'}, }; static bool is_daemon; @@ -690,11 +693,61 @@ err_out: return ret; } +/* create unix domain socket and return the descriptor */ +static int proxy_socket(const char *path, uid_t uid, gid_t gid) +{ + int sock, client; + struct sockaddr_un proxy, qemu; + socklen_t size; + + /* requested socket already exists, refuse to start */ + if (!access(path, F_OK)) { + do_log(LOG_CRIT, "socket already exists\n"); + return -1; + } + + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) { + do_perror("socket"); + return -1; + } + + /* mask other part of mode bits */ + umask(7); + + proxy.sun_family = AF_UNIX; + strcpy(proxy.sun_path, path); + if (bind(sock, (struct sockaddr *)&proxy, + sizeof(struct sockaddr_un)) < 0) { + do_perror("bind"); + return -1; + } + if (chown(proxy.sun_path, uid, gid) < 0) { + do_perror("chown"); + return -1; + } + if (listen(sock, 1) < 0) { + do_perror("listen"); + return -1; + } + + client = accept(sock, (struct sockaddr *)&qemu, &size); + if (client < 0) { + do_perror("accept"); + return -1; + } + return client; +} + static void usage(char *prog) { fprintf(stderr, "usage: %s\n" " -p|--path <path> 9p path to export\n" " {-f|--fd <socket-descriptor>} socket file descriptor to be used\n" + " {-s|--socket <socketname> socket file used for communication\n" + " \t-u|--uid <uid> -g|--gid <gid>} - uid:gid combination to give " + " access to this socket\n" + " \tNote: -s & -f can not be used together\n" " [-n|--nodaemon] Run as a normal program\n", basename(prog)); } @@ -935,16 +988,20 @@ int main(int argc, char **argv) { int sock; char *rpath = NULL; + char *sock_name = NULL; struct stat stbuf; int c, option_index; int retval; struct statfs st_fs; + uid_t own_u; + gid_t own_g; is_daemon = true; sock = -1; + own_u = own_g = -1; while (1) { option_index = 0; - c = getopt_long(argc, argv, "p:nh?f:", helper_opts, + c = getopt_long(argc, argv, "p:nh?f:s:u:g:", helper_opts, &option_index); if (c == -1) { break; @@ -959,6 +1016,15 @@ int main(int argc, char **argv) case 'f': sock = atoi(optarg); break; + case 's': + sock_name = strdup(optarg); + break; + case 'u': + own_u = atoi(optarg); + break; + case 'g': + own_g = atoi(optarg); + break; case '?': case 'h': default: @@ -968,8 +1034,16 @@ int main(int argc, char **argv) } /* Parameter validation */ - if (sock == -1 || rpath == NULL) { - fprintf(stderr, "socket descriptor or path not specified\n"); + if ((sock_name == NULL && sock == -1) || rpath == NULL) { + fprintf(stderr, "socket, socket descriptor or path not specified\n"); + usage(argv[0]); + return -1; + } + + if (*sock_name && (own_u == -1 || own_g == -1)) { + fprintf(stderr, "owner uid:gid not specified, "); + fprintf(stderr, + "owner uid:gid specifies who can access the socket file\n"); usage(argv[0]); exit(EXIT_FAILURE); } @@ -994,6 +1068,12 @@ int main(int argc, char **argv) } do_log(LOG_INFO, "Started\n"); + if (*sock_name) { + sock = proxy_socket(sock_name, own_u, own_g); + if (sock < 0) { + goto error; + } + } get_version = false; #ifdef FS_IOC_GETVERSION diff --git a/fsdev/virtfs-proxy-helper.texi b/fsdev/virtfs-proxy-helper.texi index 3816382..faa0434 100644 --- a/fsdev/virtfs-proxy-helper.texi +++ b/fsdev/virtfs-proxy-helper.texi @@ -46,6 +46,10 @@ Path to export for proxy filesystem driver Use given file descriptor as socket descriptor for communicating with qemu proxy fs drier. Usually a helper like libvirt will create socketpair and pass one of the fds as parameter to -f|--fd +@item -s|--socket socket-file +Creates named socket file for communicating with qemu proxy fs driver +@item -u|--uid uid -g|--gid gid +uid:gid combination to give access to named socket file @item -n|--nodaemon Run as a normal program. By default program will run in daemon mode @end table diff --git a/hw/9pfs/virtio-9p-proxy.c b/hw/9pfs/virtio-9p-proxy.c index e782c4f..d94db95 100644 --- a/hw/9pfs/virtio-9p-proxy.c +++ b/hw/9pfs/virtio-9p-proxy.c @@ -1095,15 +1095,49 @@ static int proxy_ioc_getversion(FsContext *fs_ctx, V9fsPath *path, return err; } +static int connect_namedsocket(const char *path) +{ + int sockfd, size; + struct sockaddr_un helper; + + sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sockfd < 0) { + fprintf(stderr, "socket %s\n", strerror(errno)); + return -1; + } + strcpy(helper.sun_path, path); + helper.sun_family = AF_UNIX; + size = strlen(helper.sun_path) + sizeof(helper.sun_family); + if (connect(sockfd, (struct sockaddr *)&helper, size) < 0) { + fprintf(stderr, "socket error\n"); + return -1; + } + + /* remove the socket for security reasons */ + unlink(path); + return sockfd; +} + static int proxy_parse_opts(QemuOpts *opts, struct FsDriverEntry *fs) { + const char *socket = qemu_opt_get(opts, "socket"); const char *sock_fd = qemu_opt_get(opts, "sock_fd"); - if (sock_fd) { - fprintf(stderr, "sock_fd option not specified\n"); + if (!socket && !sock_fd) { + fprintf(stderr, "socket and sock_fd none of the option specified\n"); + return -1; + } + if (socket && sock_fd) { + fprintf(stderr, "Both socket and sock_fd options specified\n"); return -1; } - fs->path = g_strdup(sock_fd); + if (socket) { + fs->path = g_strdup(socket); + fs->export_flags = V9FS_PROXY_SOCK_NAME; + } else { + fs->path = g_strdup(sock_fd); + fs->export_flags = V9FS_PROXY_SOCK_FD; + } return 0; } @@ -1112,10 +1146,14 @@ static int proxy_init(FsContext *ctx) V9fsProxy *proxy = g_malloc(sizeof(V9fsProxy)); int sock_id; - sock_id = atoi(ctx->fs_root); - if (sock_id < 0) { - fprintf(stderr, "socket descriptor not initialized\n"); - return -1; + if (ctx->export_flags & V9FS_PROXY_SOCK_NAME) { + sock_id = connect_namedsocket(ctx->fs_root); + } else { + sock_id = atoi(ctx->fs_root); + if (sock_id < 0) { + fprintf(stderr, "socket descriptor not initialized\n"); + return -1; + } } g_free(ctx->fs_root); diff --git a/qemu-config.c b/qemu-config.c index 1bdc01c..ecc88e8 100644 --- a/qemu-config.c +++ b/qemu-config.c @@ -211,6 +211,10 @@ QemuOptsList qemu_fsdev_opts = { }, { .name = "readonly", .type = QEMU_OPT_BOOL, + + }, { + .name = "socket", + .type = QEMU_OPT_STRING, }, { .name = "sock_fd", .type = QEMU_OPT_NUMBER, @@ -244,6 +248,9 @@ QemuOptsList qemu_virtfs_opts = { .name = "readonly", .type = QEMU_OPT_BOOL, }, { + .name = "socket", + .type = QEMU_OPT_STRING, + }, { .name = "sock_fd", .type = QEMU_OPT_NUMBER, }, diff --git a/qemu-options.hx b/qemu-options.hx index cfc999f..85c2440 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -539,12 +539,12 @@ DEFHEADING(File system options:) DEF("fsdev", HAS_ARG, QEMU_OPTION_fsdev, "-fsdev fsdriver,id=id[,path=path,][security_model={mapped|passthrough|none}]\n" - " [,writeout=immediate][,readonly][,sock_fd=sock_fd]\n", + " [,writeout=immediate][,readonly][,socket=socket|sock_fd=sock_fd]\n", QEMU_ARCH_ALL) STEXI -@item -fsdev @var{fsdriver},id=@var{id},path=@var{path},[security_model=@var{security_model}][,writeout=@var{writeout}][,readonly][,sock_fd=@var{sock_fd}] +@item -fsdev @var{fsdriver},id=@var{id},path=@var{path},[security_model=@var{security_model}][,writeout=@var{writeout}][,readonly][,socket=@var{socket}|sock_fd=@var{sock_fd}] @findex -fsdev Define a new file system device. Valid options are: @table @option @@ -577,6 +577,9 @@ reported as written by the storage subsystem. @item readonly Enables exporting 9p share as a readonly mount for guests. By default read-write access is given. +@item socket=@var{socket} +Enables proxy filesystem driver to use passed socket file for communicating +with virtfs-proxy-helper @item sock_fd=@var{sock_fd} Enables proxy filesystem driver to use passed socket descriptor for communicating with virtfs-proxy-helper. Usually a helper like libvirt @@ -601,12 +604,12 @@ DEFHEADING(Virtual File system pass-through options:) DEF("virtfs", HAS_ARG, QEMU_OPTION_virtfs, "-virtfs local,path=path,mount_tag=tag,security_model=[mapped|passthrough|none]\n" - " [,writeout=immediate][,readonly][,sock_fd=sock_fd]\n", + " [,writeout=immediate][,readonly][,socket=socket|sock_fd=sock_fd]\n", QEMU_ARCH_ALL) STEXI -@item -virtfs @var{fsdriver}[,path=@var{path}],mount_tag=@var{mount_tag}[,security_model=@var{security_model}][,writeout=@var{writeout}][,readonly][,sock_fd=@var{sock_fd}] +@item -virtfs @var{fsdriver}[,path=@var{path}],mount_tag=@var{mount_tag}[,security_model=@var{security_model}][,writeout=@var{writeout}][,readonly][,socket=@var{socket}|sock_fd=@var{sock_fd}] @findex -virtfs The general form of a Virtual File system pass-through options are: @@ -640,6 +643,10 @@ reported as written by the storage subsystem. @item readonly Enables exporting 9p share as a readonly mount for guests. By default read-write access is given. +@item socket=@var{socket} +Enables proxy filesystem driver to use passed socket file for +communicating with virtfs-proxy-helper. Usually a helper like libvirt +will create socketpair and pass one of the fds as sock_fd @item sock_fd Enables proxy filesystem driver to use passed 'sock_fd' as the socket descriptor for interfacing with virtfs-proxy-helper diff --git a/vl.c b/vl.c index 6323361..e553c2c 100644 --- a/vl.c +++ b/vl.c @@ -2669,7 +2669,7 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_virtfs: { QemuOpts *fsdev; QemuOpts *device; - const char *writeout, *sock_fd; + const char *writeout, *sock_fd, *socket; olist = qemu_find_opts("virtfs"); if (!olist) { @@ -2709,6 +2709,10 @@ int main(int argc, char **argv, char **envp) qemu_opt_set(fsdev, "path", qemu_opt_get(opts, "path")); qemu_opt_set(fsdev, "security_model", qemu_opt_get(opts, "security_model")); + socket = qemu_opt_get(opts, "socket"); + if (socket) { + qemu_opt_set(fsdev, "socket", socket); + } sock_fd = qemu_opt_get(opts, "sock_fd"); if (sock_fd) { qemu_opt_set(fsdev, "sock_fd", sock_fd); -- 1.7.6