]>
git.proxmox.com Git - mirror_qemu.git/blob - fsdev/virtfs-proxy-helper.c
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
);
235 * from man 7 capabilities, section
236 * Effect of User ID Changes on Capabilities:
237 * 4. If the file system user ID is changed from 0 to nonzero (see setfsuid(2))
238 * then the following capabilities are cleared from the effective set:
239 * CAP_CHOWN, CAP_DAC_OVERRIDE, CAP_DAC_READ_SEARCH, CAP_FOWNER, CAP_FSETID,
240 * CAP_LINUX_IMMUTABLE (since Linux 2.2.30), CAP_MAC_OVERRIDE, and CAP_MKNOD
241 * (since Linux 2.2.30). If the file system UID is changed from nonzero to 0,
242 * then any of these capabilities that are enabled in the permitted set
243 * are enabled in the effective set.
245 static int setfsugid(int uid
, int gid
)
248 * We still need DAC_OVERRIDE because we don't change
249 * supplementary group ids, and hence may be subjected DAC rules
251 cap_value_t cap_list
[] = {
258 if (uid
!= 0 || gid
!= 0) {
259 return do_cap_set(cap_list
, ARRAY_SIZE(cap_list
), 0);
265 * create a file and send fd on success
266 * return -errno on error
268 static int do_create(struct iovec
*iovec
)
272 int flags
, mode
, uid
, gid
, cur_uid
, cur_gid
;
274 v9fs_string_init(&path
);
275 ret
= proxy_unmarshal(iovec
, PROXY_HDR_SZ
, "sdddd",
276 &path
, &flags
, &mode
, &uid
, &gid
);
278 goto unmarshal_err_out
;
282 ret
= setfsugid(uid
, gid
);
285 * On failure reset back to the
291 ret
= open(path
.data
, flags
, mode
);
297 setfsugid(cur_uid
, cur_gid
);
299 v9fs_string_free(&path
);
304 * open a file and send fd on success
305 * return -errno on error
307 static int do_open(struct iovec
*iovec
)
312 v9fs_string_init(&path
);
313 ret
= proxy_unmarshal(iovec
, PROXY_HDR_SZ
, "sd", &path
, &flags
);
317 ret
= open(path
.data
, flags
);
322 v9fs_string_free(&path
);
326 static void usage(char *prog
)
328 fprintf(stderr
, "usage: %s\n"
329 " -p|--path <path> 9p path to export\n"
330 " {-f|--fd <socket-descriptor>} socket file descriptor to be used\n"
331 " [-n|--nodaemon] Run as a normal program\n",
335 static int process_reply(int sock
, int type
, int retval
)
340 if (send_fd(sock
, retval
) < 0) {
351 static int process_requests(int sock
)
355 struct iovec in_iovec
;
357 in_iovec
.iov_base
= g_malloc(PROXY_MAX_IO_SZ
+ PROXY_HDR_SZ
);
358 in_iovec
.iov_len
= PROXY_MAX_IO_SZ
+ PROXY_HDR_SZ
;
361 * initialize the header type, so that we send
362 * response to proper request type.
365 retval
= read_request(sock
, &in_iovec
, &header
);
370 switch (header
.type
) {
372 retval
= do_open(&in_iovec
);
375 retval
= do_create(&in_iovec
);
382 if (process_reply(sock
, header
.type
, retval
) < 0) {
388 g_free(in_iovec
.iov_base
);
392 int main(int argc
, char **argv
)
403 c
= getopt_long(argc
, argv
, "p:nh?f:", helper_opts
,
410 rpath
= strdup(optarg
);
426 /* Parameter validation */
427 if (sock
== -1 || rpath
== NULL
) {
428 fprintf(stderr
, "socket descriptor or path not specified\n");
433 if (lstat(rpath
, &stbuf
) < 0) {
434 fprintf(stderr
, "invalid path \"%s\" specified, %s\n",
435 rpath
, strerror(errno
));
439 if (!S_ISDIR(stbuf
.st_mode
)) {
440 fprintf(stderr
, "specified path \"%s\" is not directory\n", rpath
);
445 if (daemon(0, 0) < 0) {
446 fprintf(stderr
, "daemon call failed\n");
449 openlog(PROGNAME
, LOG_PID
, LOG_DAEMON
);
452 do_log(LOG_INFO
, "Started\n");
454 if (chdir("/") < 0) {
458 if (chroot(rpath
) < 0) {
464 if (init_capabilities() < 0) {
468 process_requests(sock
);
470 do_log(LOG_INFO
, "Done\n");