]>
git.proxmox.com Git - qemu.git/blob - fsdev/virtfs-proxy-helper.c
83fbae7f5f179e65145255090717fba4c4e89f2f
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>
28 #include "qemu-common.h"
29 #include "virtio-9p-marshal.h"
30 #include "hw/9pfs/virtio-9p-proxy.h"
31 #include "fsdev/virtio-9p-marshal.h"
33 #define PROGNAME "virtfs-proxy-helper"
35 static struct option helper_opts
[] = {
36 {"fd", required_argument
, NULL
, 'f'},
37 {"path", required_argument
, NULL
, 'p'},
38 {"nodaemon", no_argument
, NULL
, 'n'},
41 static bool is_daemon
;
43 static void do_log(int loglevel
, const char *format
, ...)
49 vsyslog(LOG_CRIT
, format
, ap
);
51 vfprintf(stderr
, format
, ap
);
56 static void do_perror(const char *string
)
59 syslog(LOG_CRIT
, "%s:%s", string
, strerror(errno
));
61 fprintf(stderr
, "%s:%s\n", string
, strerror(errno
));
65 static int do_cap_set(cap_value_t
*cap_value
, int size
, int reset
)
70 * Start with an empty set and set permitted and effective
74 do_perror("cap_init");
77 if (cap_set_flag(caps
, CAP_PERMITTED
, size
, cap_value
, CAP_SET
) < 0) {
78 do_perror("cap_set_flag");
82 caps
= cap_get_proc();
84 do_perror("cap_get_proc");
88 if (cap_set_flag(caps
, CAP_EFFECTIVE
, size
, cap_value
, CAP_SET
) < 0) {
89 do_perror("cap_set_flag");
92 if (cap_set_proc(caps
) < 0) {
93 do_perror("cap_set_proc");
104 static int init_capabilities(void)
106 /* helper needs following capbabilities only */
107 cap_value_t cap_list
[] = {
116 return do_cap_set(cap_list
, ARRAY_SIZE(cap_list
), 1);
119 static int socket_read(int sockfd
, void *buff
, ssize_t size
)
121 ssize_t retval
, total
= 0;
124 retval
= read(sockfd
, buff
, size
);
129 if (errno
== EINTR
) {
141 static int socket_write(int sockfd
, void *buff
, ssize_t size
)
143 ssize_t retval
, total
= 0;
146 retval
= write(sockfd
, buff
, size
);
148 if (errno
== EINTR
) {
160 static int read_request(int sockfd
, struct iovec
*iovec
, ProxyHeader
*header
)
165 * read the request header.
168 retval
= socket_read(sockfd
, iovec
->iov_base
, PROXY_HDR_SZ
);
172 iovec
->iov_len
= PROXY_HDR_SZ
;
173 retval
= proxy_unmarshal(iovec
, 0, "dd", &header
->type
, &header
->size
);
178 * We can't process message.size > PROXY_MAX_IO_SZ.
179 * Treat it as fatal error
181 if (header
->size
> PROXY_MAX_IO_SZ
) {
184 retval
= socket_read(sockfd
, iovec
->iov_base
+ PROXY_HDR_SZ
, header
->size
);
188 iovec
->iov_len
+= header
->size
;
192 static int send_fd(int sockfd
, int fd
)
197 struct cmsghdr
*cmsg
;
198 union MsgControl msg_control
;
200 iov
.iov_base
= &data
;
201 iov
.iov_len
= sizeof(data
);
203 memset(&msg
, 0, sizeof(msg
));
206 /* No ancillary data on error */
208 /* fd is really negative errno if the request failed */
211 data
= V9FS_FD_VALID
;
212 msg
.msg_control
= &msg_control
;
213 msg
.msg_controllen
= sizeof(msg_control
);
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
));
223 retval
= sendmsg(sockfd
, &msg
, 0);
224 } while (retval
< 0 && errno
== EINTR
);
234 static int send_status(int sockfd
, struct iovec
*iovec
, int status
)
237 int retval
, msg_size
;;
240 header
.type
= T_ERROR
;
242 header
.type
= T_SUCCESS
;
244 header
.size
= sizeof(status
);
246 * marshal the return status. We don't check error.
247 * because we are sure we have enough space for the status
249 msg_size
= proxy_marshal(iovec
, 0, "ddd", header
.type
,
250 header
.size
, status
);
251 retval
= socket_write(sockfd
, iovec
->iov_base
, msg_size
);
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.
269 static int setfsugid(int uid
, int gid
)
272 * We still need DAC_OVERRIDE because we don't change
273 * supplementary group ids, and hence may be subjected DAC rules
275 cap_value_t cap_list
[] = {
282 if (uid
!= 0 || gid
!= 0) {
283 return do_cap_set(cap_list
, ARRAY_SIZE(cap_list
), 0);
289 * create other filesystem objects and send 0 on success
290 * return -errno on error
292 static int do_create_others(int type
, struct iovec
*iovec
)
296 int offset
= PROXY_HDR_SZ
;
297 V9fsString oldpath
, path
;
298 int mode
, uid
, gid
, cur_uid
, cur_gid
;
300 v9fs_string_init(&path
);
301 v9fs_string_init(&oldpath
);
305 retval
= proxy_unmarshal(iovec
, offset
, "dd", &uid
, &gid
);
310 retval
= setfsugid(uid
, gid
);
317 retval
= proxy_unmarshal(iovec
, offset
, "sdq", &path
, &mode
, &rdev
);
321 retval
= mknod(path
.data
, mode
, rdev
);
324 retval
= proxy_unmarshal(iovec
, offset
, "sd", &path
, &mode
);
328 retval
= mkdir(path
.data
, mode
);
331 retval
= proxy_unmarshal(iovec
, offset
, "ss", &oldpath
, &path
);
335 retval
= symlink(oldpath
.data
, path
.data
);
343 v9fs_string_free(&path
);
344 v9fs_string_free(&oldpath
);
345 setfsugid(cur_uid
, cur_gid
);
350 * create a file and send fd on success
351 * return -errno on error
353 static int do_create(struct iovec
*iovec
)
357 int flags
, mode
, uid
, gid
, cur_uid
, cur_gid
;
359 v9fs_string_init(&path
);
360 ret
= proxy_unmarshal(iovec
, PROXY_HDR_SZ
, "sdddd",
361 &path
, &flags
, &mode
, &uid
, &gid
);
363 goto unmarshal_err_out
;
367 ret
= setfsugid(uid
, gid
);
370 * On failure reset back to the
376 ret
= open(path
.data
, flags
, mode
);
382 setfsugid(cur_uid
, cur_gid
);
384 v9fs_string_free(&path
);
389 * open a file and send fd on success
390 * return -errno on error
392 static int do_open(struct iovec
*iovec
)
397 v9fs_string_init(&path
);
398 ret
= proxy_unmarshal(iovec
, PROXY_HDR_SZ
, "sd", &path
, &flags
);
402 ret
= open(path
.data
, flags
);
407 v9fs_string_free(&path
);
411 static void usage(char *prog
)
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",
420 static int process_reply(int sock
, int type
,
421 struct iovec
*out_iovec
, int retval
)
426 if (send_fd(sock
, retval
) < 0) {
434 if (send_status(sock
, out_iovec
, retval
) < 0) {
445 static int process_requests(int sock
)
449 V9fsString oldpath
, path
;
450 struct iovec in_iovec
, out_iovec
;
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
;
459 * initialize the header type, so that we send
460 * response to proper request type.
463 retval
= read_request(sock
, &in_iovec
, &header
);
468 switch (header
.type
) {
470 retval
= do_open(&in_iovec
);
473 retval
= do_create(&in_iovec
);
478 retval
= do_create_others(header
.type
, &in_iovec
);
481 v9fs_string_init(&path
);
482 v9fs_string_init(&oldpath
);
483 retval
= proxy_unmarshal(&in_iovec
, PROXY_HDR_SZ
,
484 "ss", &oldpath
, &path
);
486 retval
= link(oldpath
.data
, path
.data
);
491 v9fs_string_free(&oldpath
);
492 v9fs_string_free(&path
);
499 if (process_reply(sock
, header
.type
, &out_iovec
, retval
) < 0) {
504 g_free(in_iovec
.iov_base
);
505 g_free(out_iovec
.iov_base
);
509 int main(int argc
, char **argv
)
520 c
= getopt_long(argc
, argv
, "p:nh?f:", helper_opts
,
527 rpath
= strdup(optarg
);
543 /* Parameter validation */
544 if (sock
== -1 || rpath
== NULL
) {
545 fprintf(stderr
, "socket descriptor or path not specified\n");
550 if (lstat(rpath
, &stbuf
) < 0) {
551 fprintf(stderr
, "invalid path \"%s\" specified, %s\n",
552 rpath
, strerror(errno
));
556 if (!S_ISDIR(stbuf
.st_mode
)) {
557 fprintf(stderr
, "specified path \"%s\" is not directory\n", rpath
);
562 if (daemon(0, 0) < 0) {
563 fprintf(stderr
, "daemon call failed\n");
566 openlog(PROGNAME
, LOG_PID
, LOG_DAEMON
);
569 do_log(LOG_INFO
, "Started\n");
571 if (chdir("/") < 0) {
575 if (chroot(rpath
) < 0) {
581 if (init_capabilities() < 0) {
585 process_requests(sock
);
587 do_log(LOG_INFO
, "Done\n");