]> git.proxmox.com Git - mirror_qemu.git/blob - fsdev/virtfs-proxy-helper.c
hw/9pfs: File system helper process for qemu 9p proxy FS
[mirror_qemu.git] / fsdev / virtfs-proxy-helper.c
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>
12 #include <string.h>
13 #include <sys/un.h>
14 #include <limits.h>
15 #include <signal.h>
16 #include <errno.h>
17 #include <stdlib.h>
18 #include <sys/resource.h>
19 #include <sys/stat.h>
20 #include <getopt.h>
21 #include <unistd.h>
22 #include <syslog.h>
23 #include <sys/capability.h>
24 #include <sys/fsuid.h>
25 #include <stdarg.h>
26 #include <stdbool.h>
27 #include "qemu-common.h"
28 #include "virtio-9p-marshal.h"
29 #include "hw/9pfs/virtio-9p-proxy.h"
30
31 #define PROGNAME "virtfs-proxy-helper"
32
33 static struct option helper_opts[] = {
34 {"fd", required_argument, NULL, 'f'},
35 {"path", required_argument, NULL, 'p'},
36 {"nodaemon", no_argument, NULL, 'n'},
37 };
38
39 static bool is_daemon;
40
41 static void do_log(int loglevel, const char *format, ...)
42 {
43 va_list ap;
44
45 va_start(ap, format);
46 if (is_daemon) {
47 vsyslog(LOG_CRIT, format, ap);
48 } else {
49 vfprintf(stderr, format, ap);
50 }
51 va_end(ap);
52 }
53
54 static void do_perror(const char *string)
55 {
56 if (is_daemon) {
57 syslog(LOG_CRIT, "%s:%s", string, strerror(errno));
58 } else {
59 fprintf(stderr, "%s:%s\n", string, strerror(errno));
60 }
61 }
62
63 static int do_cap_set(cap_value_t *cap_value, int size, int reset)
64 {
65 cap_t caps;
66 if (reset) {
67 /*
68 * Start with an empty set and set permitted and effective
69 */
70 caps = cap_init();
71 if (caps == NULL) {
72 do_perror("cap_init");
73 return -1;
74 }
75 if (cap_set_flag(caps, CAP_PERMITTED, size, cap_value, CAP_SET) < 0) {
76 do_perror("cap_set_flag");
77 goto error;
78 }
79 } else {
80 caps = cap_get_proc();
81 if (!caps) {
82 do_perror("cap_get_proc");
83 return -1;
84 }
85 }
86 if (cap_set_flag(caps, CAP_EFFECTIVE, size, cap_value, CAP_SET) < 0) {
87 do_perror("cap_set_flag");
88 goto error;
89 }
90 if (cap_set_proc(caps) < 0) {
91 do_perror("cap_set_proc");
92 goto error;
93 }
94 cap_free(caps);
95 return 0;
96
97 error:
98 cap_free(caps);
99 return -1;
100 }
101
102 static int init_capabilities(void)
103 {
104 /* helper needs following capbabilities only */
105 cap_value_t cap_list[] = {
106 CAP_CHOWN,
107 CAP_DAC_OVERRIDE,
108 CAP_FOWNER,
109 CAP_FSETID,
110 CAP_SETGID,
111 CAP_MKNOD,
112 CAP_SETUID,
113 };
114 return do_cap_set(cap_list, ARRAY_SIZE(cap_list), 1);
115 }
116
117 static int socket_read(int sockfd, void *buff, ssize_t size)
118 {
119 ssize_t retval, total = 0;
120
121 while (size) {
122 retval = read(sockfd, buff, size);
123 if (retval == 0) {
124 return -EIO;
125 }
126 if (retval < 0) {
127 if (errno == EINTR) {
128 continue;
129 }
130 return -errno;
131 }
132 size -= retval;
133 buff += retval;
134 total += retval;
135 }
136 return total;
137 }
138
139 static int socket_write(int sockfd, void *buff, ssize_t size)
140 {
141 ssize_t retval, total = 0;
142
143 while (size) {
144 retval = write(sockfd, buff, size);
145 if (retval < 0) {
146 if (errno == EINTR) {
147 continue;
148 }
149 return -errno;
150 }
151 size -= retval;
152 buff += retval;
153 total += retval;
154 }
155 return total;
156 }
157
158 static int read_request(int sockfd, struct iovec *iovec, ProxyHeader *header)
159 {
160 int retval;
161
162 /*
163 * read the request header.
164 */
165 iovec->iov_len = 0;
166 retval = socket_read(sockfd, iovec->iov_base, PROXY_HDR_SZ);
167 if (retval < 0) {
168 return retval;
169 }
170 iovec->iov_len = PROXY_HDR_SZ;
171 retval = proxy_unmarshal(iovec, 0, "dd", &header->type, &header->size);
172 if (retval < 0) {
173 return retval;
174 }
175 /*
176 * We can't process message.size > PROXY_MAX_IO_SZ.
177 * Treat it as fatal error
178 */
179 if (header->size > PROXY_MAX_IO_SZ) {
180 return -ENOBUFS;
181 }
182 retval = socket_read(sockfd, iovec->iov_base + PROXY_HDR_SZ, header->size);
183 if (retval < 0) {
184 return retval;
185 }
186 iovec->iov_len += header->size;
187 return 0;
188 }
189
190 static void usage(char *prog)
191 {
192 fprintf(stderr, "usage: %s\n"
193 " -p|--path <path> 9p path to export\n"
194 " {-f|--fd <socket-descriptor>} socket file descriptor to be used\n"
195 " [-n|--nodaemon] Run as a normal program\n",
196 basename(prog));
197 }
198
199 static int process_requests(int sock)
200 {
201 int retval;
202 ProxyHeader header;
203 struct iovec in_iovec;
204
205 in_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ);
206 in_iovec.iov_len = PROXY_MAX_IO_SZ + PROXY_HDR_SZ;
207 while (1) {
208 retval = read_request(sock, &in_iovec, &header);
209 if (retval < 0) {
210 goto error;
211 }
212 }
213 (void)socket_write;
214 error:
215 g_free(in_iovec.iov_base);
216 return -1;
217 }
218
219 int main(int argc, char **argv)
220 {
221 int sock;
222 char *rpath = NULL;
223 struct stat stbuf;
224 int c, option_index;
225
226 is_daemon = true;
227 sock = -1;
228 while (1) {
229 option_index = 0;
230 c = getopt_long(argc, argv, "p:nh?f:", helper_opts,
231 &option_index);
232 if (c == -1) {
233 break;
234 }
235 switch (c) {
236 case 'p':
237 rpath = strdup(optarg);
238 break;
239 case 'n':
240 is_daemon = false;
241 break;
242 case 'f':
243 sock = atoi(optarg);
244 break;
245 case '?':
246 case 'h':
247 default:
248 usage(argv[0]);
249 exit(EXIT_FAILURE);
250 }
251 }
252
253 /* Parameter validation */
254 if (sock == -1 || rpath == NULL) {
255 fprintf(stderr, "socket descriptor or path not specified\n");
256 usage(argv[0]);
257 exit(EXIT_FAILURE);
258 }
259
260 if (lstat(rpath, &stbuf) < 0) {
261 fprintf(stderr, "invalid path \"%s\" specified, %s\n",
262 rpath, strerror(errno));
263 exit(EXIT_FAILURE);
264 }
265
266 if (!S_ISDIR(stbuf.st_mode)) {
267 fprintf(stderr, "specified path \"%s\" is not directory\n", rpath);
268 exit(EXIT_FAILURE);
269 }
270
271 if (is_daemon) {
272 if (daemon(0, 0) < 0) {
273 fprintf(stderr, "daemon call failed\n");
274 exit(EXIT_FAILURE);
275 }
276 openlog(PROGNAME, LOG_PID, LOG_DAEMON);
277 }
278
279 do_log(LOG_INFO, "Started\n");
280
281 if (chdir("/") < 0) {
282 do_perror("chdir");
283 goto error;
284 }
285 if (chroot(rpath) < 0) {
286 do_perror("chroot");
287 goto error;
288 }
289 umask(0);
290
291 if (init_capabilities() < 0) {
292 goto error;
293 }
294
295 process_requests(sock);
296 error:
297 do_log(LOG_INFO, "Done\n");
298 closelog();
299 return 0;
300 }