root / fsdev / virtfs-proxy-helper.c @ 1de7afc9
History | View | Annotate | Download (30.3 kB)
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 |
|
12 |
#include <sys/resource.h> |
13 |
#include <getopt.h> |
14 |
#include <syslog.h> |
15 |
#include <sys/capability.h> |
16 |
#include <sys/fsuid.h> |
17 |
#include <sys/vfs.h> |
18 |
#include <sys/ioctl.h> |
19 |
#include <linux/fs.h> |
20 |
#ifdef CONFIG_LINUX_MAGIC_H
|
21 |
#include <linux/magic.h> |
22 |
#endif
|
23 |
#include "qemu-common.h" |
24 |
#include "qemu/sockets.h" |
25 |
#include "qemu/xattr.h" |
26 |
#include "virtio-9p-marshal.h" |
27 |
#include "hw/9pfs/virtio-9p-proxy.h" |
28 |
#include "fsdev/virtio-9p-marshal.h" |
29 |
|
30 |
#define PROGNAME "virtfs-proxy-helper" |
31 |
|
32 |
#ifndef XFS_SUPER_MAGIC
|
33 |
#define XFS_SUPER_MAGIC 0x58465342 |
34 |
#endif
|
35 |
#ifndef EXT2_SUPER_MAGIC
|
36 |
#define EXT2_SUPER_MAGIC 0xEF53 |
37 |
#endif
|
38 |
#ifndef REISERFS_SUPER_MAGIC
|
39 |
#define REISERFS_SUPER_MAGIC 0x52654973 |
40 |
#endif
|
41 |
#ifndef BTRFS_SUPER_MAGIC
|
42 |
#define BTRFS_SUPER_MAGIC 0x9123683E |
43 |
#endif
|
44 |
|
45 |
static struct option helper_opts[] = { |
46 |
{"fd", required_argument, NULL, 'f'}, |
47 |
{"path", required_argument, NULL, 'p'}, |
48 |
{"nodaemon", no_argument, NULL, 'n'}, |
49 |
{"socket", required_argument, NULL, 's'}, |
50 |
{"uid", required_argument, NULL, 'u'}, |
51 |
{"gid", required_argument, NULL, 'g'}, |
52 |
}; |
53 |
|
54 |
static bool is_daemon; |
55 |
static bool get_version; /* IOC getversion IOCTL supported */ |
56 |
|
57 |
static void GCC_FMT_ATTR(2, 3) do_log(int loglevel, const char *format, ...) |
58 |
{ |
59 |
va_list ap; |
60 |
|
61 |
va_start(ap, format); |
62 |
if (is_daemon) {
|
63 |
vsyslog(LOG_CRIT, format, ap); |
64 |
} else {
|
65 |
vfprintf(stderr, format, ap); |
66 |
} |
67 |
va_end(ap); |
68 |
} |
69 |
|
70 |
static void do_perror(const char *string) |
71 |
{ |
72 |
if (is_daemon) {
|
73 |
syslog(LOG_CRIT, "%s:%s", string, strerror(errno));
|
74 |
} else {
|
75 |
fprintf(stderr, "%s:%s\n", string, strerror(errno));
|
76 |
} |
77 |
} |
78 |
|
79 |
static int do_cap_set(cap_value_t *cap_value, int size, int reset) |
80 |
{ |
81 |
cap_t caps; |
82 |
if (reset) {
|
83 |
/*
|
84 |
* Start with an empty set and set permitted and effective
|
85 |
*/
|
86 |
caps = cap_init(); |
87 |
if (caps == NULL) { |
88 |
do_perror("cap_init");
|
89 |
return -1; |
90 |
} |
91 |
if (cap_set_flag(caps, CAP_PERMITTED, size, cap_value, CAP_SET) < 0) { |
92 |
do_perror("cap_set_flag");
|
93 |
goto error;
|
94 |
} |
95 |
} else {
|
96 |
caps = cap_get_proc(); |
97 |
if (!caps) {
|
98 |
do_perror("cap_get_proc");
|
99 |
return -1; |
100 |
} |
101 |
} |
102 |
if (cap_set_flag(caps, CAP_EFFECTIVE, size, cap_value, CAP_SET) < 0) { |
103 |
do_perror("cap_set_flag");
|
104 |
goto error;
|
105 |
} |
106 |
if (cap_set_proc(caps) < 0) { |
107 |
do_perror("cap_set_proc");
|
108 |
goto error;
|
109 |
} |
110 |
cap_free(caps); |
111 |
return 0; |
112 |
|
113 |
error:
|
114 |
cap_free(caps); |
115 |
return -1; |
116 |
} |
117 |
|
118 |
static int init_capabilities(void) |
119 |
{ |
120 |
/* helper needs following capbabilities only */
|
121 |
cap_value_t cap_list[] = { |
122 |
CAP_CHOWN, |
123 |
CAP_DAC_OVERRIDE, |
124 |
CAP_FOWNER, |
125 |
CAP_FSETID, |
126 |
CAP_SETGID, |
127 |
CAP_MKNOD, |
128 |
CAP_SETUID, |
129 |
}; |
130 |
return do_cap_set(cap_list, ARRAY_SIZE(cap_list), 1); |
131 |
} |
132 |
|
133 |
static int socket_read(int sockfd, void *buff, ssize_t size) |
134 |
{ |
135 |
ssize_t retval, total = 0;
|
136 |
|
137 |
while (size) {
|
138 |
retval = read(sockfd, buff, size); |
139 |
if (retval == 0) { |
140 |
return -EIO;
|
141 |
} |
142 |
if (retval < 0) { |
143 |
if (errno == EINTR) {
|
144 |
continue;
|
145 |
} |
146 |
return -errno;
|
147 |
} |
148 |
size -= retval; |
149 |
buff += retval; |
150 |
total += retval; |
151 |
} |
152 |
return total;
|
153 |
} |
154 |
|
155 |
static int socket_write(int sockfd, void *buff, ssize_t size) |
156 |
{ |
157 |
ssize_t retval, total = 0;
|
158 |
|
159 |
while (size) {
|
160 |
retval = write(sockfd, buff, size); |
161 |
if (retval < 0) { |
162 |
if (errno == EINTR) {
|
163 |
continue;
|
164 |
} |
165 |
return -errno;
|
166 |
} |
167 |
size -= retval; |
168 |
buff += retval; |
169 |
total += retval; |
170 |
} |
171 |
return total;
|
172 |
} |
173 |
|
174 |
static int read_request(int sockfd, struct iovec *iovec, ProxyHeader *header) |
175 |
{ |
176 |
int retval;
|
177 |
|
178 |
/*
|
179 |
* read the request header.
|
180 |
*/
|
181 |
iovec->iov_len = 0;
|
182 |
retval = socket_read(sockfd, iovec->iov_base, PROXY_HDR_SZ); |
183 |
if (retval < 0) { |
184 |
return retval;
|
185 |
} |
186 |
iovec->iov_len = PROXY_HDR_SZ; |
187 |
retval = proxy_unmarshal(iovec, 0, "dd", &header->type, &header->size); |
188 |
if (retval < 0) { |
189 |
return retval;
|
190 |
} |
191 |
/*
|
192 |
* We can't process message.size > PROXY_MAX_IO_SZ.
|
193 |
* Treat it as fatal error
|
194 |
*/
|
195 |
if (header->size > PROXY_MAX_IO_SZ) {
|
196 |
return -ENOBUFS;
|
197 |
} |
198 |
retval = socket_read(sockfd, iovec->iov_base + PROXY_HDR_SZ, header->size); |
199 |
if (retval < 0) { |
200 |
return retval;
|
201 |
} |
202 |
iovec->iov_len += header->size; |
203 |
return 0; |
204 |
} |
205 |
|
206 |
static int send_fd(int sockfd, int fd) |
207 |
{ |
208 |
struct msghdr msg;
|
209 |
struct iovec iov;
|
210 |
int retval, data;
|
211 |
struct cmsghdr *cmsg;
|
212 |
union MsgControl msg_control;
|
213 |
|
214 |
iov.iov_base = &data; |
215 |
iov.iov_len = sizeof(data);
|
216 |
|
217 |
memset(&msg, 0, sizeof(msg)); |
218 |
msg.msg_iov = &iov; |
219 |
msg.msg_iovlen = 1;
|
220 |
/* No ancillary data on error */
|
221 |
if (fd < 0) { |
222 |
/* fd is really negative errno if the request failed */
|
223 |
data = fd; |
224 |
} else {
|
225 |
data = V9FS_FD_VALID; |
226 |
msg.msg_control = &msg_control; |
227 |
msg.msg_controllen = sizeof(msg_control);
|
228 |
|
229 |
cmsg = &msg_control.cmsg; |
230 |
cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
|
231 |
cmsg->cmsg_level = SOL_SOCKET; |
232 |
cmsg->cmsg_type = SCM_RIGHTS; |
233 |
memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
|
234 |
} |
235 |
|
236 |
do {
|
237 |
retval = sendmsg(sockfd, &msg, 0);
|
238 |
} while (retval < 0 && errno == EINTR); |
239 |
if (fd >= 0) { |
240 |
close(fd); |
241 |
} |
242 |
if (retval < 0) { |
243 |
return retval;
|
244 |
} |
245 |
return 0; |
246 |
} |
247 |
|
248 |
static int send_status(int sockfd, struct iovec *iovec, int status) |
249 |
{ |
250 |
ProxyHeader header; |
251 |
int retval, msg_size;;
|
252 |
|
253 |
if (status < 0) { |
254 |
header.type = T_ERROR; |
255 |
} else {
|
256 |
header.type = T_SUCCESS; |
257 |
} |
258 |
header.size = sizeof(status);
|
259 |
/*
|
260 |
* marshal the return status. We don't check error.
|
261 |
* because we are sure we have enough space for the status
|
262 |
*/
|
263 |
msg_size = proxy_marshal(iovec, 0, "ddd", header.type, |
264 |
header.size, status); |
265 |
retval = socket_write(sockfd, iovec->iov_base, msg_size); |
266 |
if (retval < 0) { |
267 |
return retval;
|
268 |
} |
269 |
return 0; |
270 |
} |
271 |
|
272 |
/*
|
273 |
* from man 7 capabilities, section
|
274 |
* Effect of User ID Changes on Capabilities:
|
275 |
* If the effective user ID is changed from nonzero to 0, then the permitted
|
276 |
* set is copied to the effective set. If the effective user ID is changed
|
277 |
* from 0 to nonzero, then all capabilities are are cleared from the effective
|
278 |
* set.
|
279 |
*
|
280 |
* The setfsuid/setfsgid man pages warn that changing the effective user ID may
|
281 |
* expose the program to unwanted signals, but this is not true anymore: for an
|
282 |
* unprivileged (without CAP_KILL) program to send a signal, the real or
|
283 |
* effective user ID of the sending process must equal the real or saved user
|
284 |
* ID of the target process. Even when dropping privileges, it is enough to
|
285 |
* keep the saved UID to a "privileged" value and virtfs-proxy-helper won't
|
286 |
* be exposed to signals. So just use setresuid/setresgid.
|
287 |
*/
|
288 |
static int setugid(int uid, int gid, int *suid, int *sgid) |
289 |
{ |
290 |
int retval;
|
291 |
|
292 |
/*
|
293 |
* We still need DAC_OVERRIDE because we don't change
|
294 |
* supplementary group ids, and hence may be subjected DAC rules
|
295 |
*/
|
296 |
cap_value_t cap_list[] = { |
297 |
CAP_DAC_OVERRIDE, |
298 |
}; |
299 |
|
300 |
*suid = geteuid(); |
301 |
*sgid = getegid(); |
302 |
|
303 |
if (setresgid(-1, gid, *sgid) == -1) { |
304 |
retval = -errno; |
305 |
goto err_out;
|
306 |
} |
307 |
|
308 |
if (setresuid(-1, uid, *suid) == -1) { |
309 |
retval = -errno; |
310 |
goto err_sgid;
|
311 |
} |
312 |
|
313 |
if (uid != 0 || gid != 0) { |
314 |
if (do_cap_set(cap_list, ARRAY_SIZE(cap_list), 0) < 0) { |
315 |
retval = -errno; |
316 |
goto err_suid;
|
317 |
} |
318 |
} |
319 |
return 0; |
320 |
|
321 |
err_suid:
|
322 |
if (setresuid(-1, *suid, *suid) == -1) { |
323 |
abort(); |
324 |
} |
325 |
err_sgid:
|
326 |
if (setresgid(-1, *sgid, *sgid) == -1) { |
327 |
abort(); |
328 |
} |
329 |
err_out:
|
330 |
return retval;
|
331 |
} |
332 |
|
333 |
/*
|
334 |
* This is used to reset the ugid back with the saved values
|
335 |
* There is nothing much we can do checking error values here.
|
336 |
*/
|
337 |
static void resetugid(int suid, int sgid) |
338 |
{ |
339 |
if (setresgid(-1, sgid, sgid) == -1) { |
340 |
abort(); |
341 |
} |
342 |
if (setresuid(-1, suid, suid) == -1) { |
343 |
abort(); |
344 |
} |
345 |
} |
346 |
|
347 |
/*
|
348 |
* send response in two parts
|
349 |
* 1) ProxyHeader
|
350 |
* 2) Response or error status
|
351 |
* This function should be called with marshaled response
|
352 |
* send_response constructs header part and error part only.
|
353 |
* send response sends {ProxyHeader,Response} if the request was success
|
354 |
* otherwise sends {ProxyHeader,error status}
|
355 |
*/
|
356 |
static int send_response(int sock, struct iovec *iovec, int size) |
357 |
{ |
358 |
int retval;
|
359 |
ProxyHeader header; |
360 |
|
361 |
/*
|
362 |
* If response size exceeds available iovec->iov_len,
|
363 |
* we return ENOBUFS
|
364 |
*/
|
365 |
if (size > PROXY_MAX_IO_SZ) {
|
366 |
size = -ENOBUFS; |
367 |
} |
368 |
|
369 |
if (size < 0) { |
370 |
/*
|
371 |
* In case of error we would not have got the error encoded
|
372 |
* already so encode the error here.
|
373 |
*/
|
374 |
header.type = T_ERROR; |
375 |
header.size = sizeof(size);
|
376 |
proxy_marshal(iovec, PROXY_HDR_SZ, "d", size);
|
377 |
} else {
|
378 |
header.type = T_SUCCESS; |
379 |
header.size = size; |
380 |
} |
381 |
proxy_marshal(iovec, 0, "dd", header.type, header.size); |
382 |
retval = socket_write(sock, iovec->iov_base, header.size + PROXY_HDR_SZ); |
383 |
if (retval < 0) { |
384 |
return retval;;
|
385 |
} |
386 |
return 0; |
387 |
} |
388 |
|
389 |
/*
|
390 |
* gets generation number
|
391 |
* returns -errno on failure and sizeof(generation number) on success
|
392 |
*/
|
393 |
static int do_getversion(struct iovec *iovec, struct iovec *out_iovec) |
394 |
{ |
395 |
uint64_t version; |
396 |
int retval = -ENOTTY;
|
397 |
#ifdef FS_IOC_GETVERSION
|
398 |
int fd;
|
399 |
V9fsString path; |
400 |
#endif
|
401 |
|
402 |
|
403 |
/* no need to issue ioctl */
|
404 |
if (!get_version) {
|
405 |
version = 0;
|
406 |
retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "q", version);
|
407 |
return retval;
|
408 |
} |
409 |
#ifdef FS_IOC_GETVERSION
|
410 |
retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "s", &path);
|
411 |
if (retval < 0) { |
412 |
return retval;
|
413 |
} |
414 |
|
415 |
fd = open(path.data, O_RDONLY); |
416 |
if (fd < 0) { |
417 |
retval = -errno; |
418 |
goto err_out;
|
419 |
} |
420 |
if (ioctl(fd, FS_IOC_GETVERSION, &version) < 0) { |
421 |
retval = -errno; |
422 |
} else {
|
423 |
retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "q", version);
|
424 |
} |
425 |
close(fd); |
426 |
err_out:
|
427 |
v9fs_string_free(&path); |
428 |
#endif
|
429 |
return retval;
|
430 |
} |
431 |
|
432 |
static int do_getxattr(int type, struct iovec *iovec, struct iovec *out_iovec) |
433 |
{ |
434 |
int size = 0, offset, retval; |
435 |
V9fsString path, name, xattr; |
436 |
|
437 |
v9fs_string_init(&xattr); |
438 |
v9fs_string_init(&path); |
439 |
retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "ds", &size, &path);
|
440 |
if (retval < 0) { |
441 |
return retval;
|
442 |
} |
443 |
offset = PROXY_HDR_SZ + retval; |
444 |
|
445 |
if (size) {
|
446 |
xattr.data = g_malloc(size); |
447 |
xattr.size = size; |
448 |
} |
449 |
switch (type) {
|
450 |
case T_LGETXATTR:
|
451 |
v9fs_string_init(&name); |
452 |
retval = proxy_unmarshal(iovec, offset, "s", &name);
|
453 |
if (retval > 0) { |
454 |
retval = lgetxattr(path.data, name.data, xattr.data, size); |
455 |
if (retval < 0) { |
456 |
retval = -errno; |
457 |
} else {
|
458 |
xattr.size = retval; |
459 |
} |
460 |
} |
461 |
v9fs_string_free(&name); |
462 |
break;
|
463 |
case T_LLISTXATTR:
|
464 |
retval = llistxattr(path.data, xattr.data, size); |
465 |
if (retval < 0) { |
466 |
retval = -errno; |
467 |
} else {
|
468 |
xattr.size = retval; |
469 |
} |
470 |
break;
|
471 |
} |
472 |
if (retval < 0) { |
473 |
goto err_out;
|
474 |
} |
475 |
|
476 |
if (!size) {
|
477 |
proxy_marshal(out_iovec, PROXY_HDR_SZ, "d", retval);
|
478 |
retval = sizeof(retval);
|
479 |
} else {
|
480 |
retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "s", &xattr);
|
481 |
} |
482 |
err_out:
|
483 |
v9fs_string_free(&xattr); |
484 |
v9fs_string_free(&path); |
485 |
return retval;
|
486 |
} |
487 |
|
488 |
static void stat_to_prstat(ProxyStat *pr_stat, struct stat *stat) |
489 |
{ |
490 |
memset(pr_stat, 0, sizeof(*pr_stat)); |
491 |
pr_stat->st_dev = stat->st_dev; |
492 |
pr_stat->st_ino = stat->st_ino; |
493 |
pr_stat->st_nlink = stat->st_nlink; |
494 |
pr_stat->st_mode = stat->st_mode; |
495 |
pr_stat->st_uid = stat->st_uid; |
496 |
pr_stat->st_gid = stat->st_gid; |
497 |
pr_stat->st_rdev = stat->st_rdev; |
498 |
pr_stat->st_size = stat->st_size; |
499 |
pr_stat->st_blksize = stat->st_blksize; |
500 |
pr_stat->st_blocks = stat->st_blocks; |
501 |
pr_stat->st_atim_sec = stat->st_atim.tv_sec; |
502 |
pr_stat->st_atim_nsec = stat->st_atim.tv_nsec; |
503 |
pr_stat->st_mtim_sec = stat->st_mtim.tv_sec; |
504 |
pr_stat->st_mtim_nsec = stat->st_mtim.tv_nsec; |
505 |
pr_stat->st_ctim_sec = stat->st_ctim.tv_sec; |
506 |
pr_stat->st_ctim_nsec = stat->st_ctim.tv_nsec; |
507 |
} |
508 |
|
509 |
static void statfs_to_prstatfs(ProxyStatFS *pr_stfs, struct statfs *stfs) |
510 |
{ |
511 |
memset(pr_stfs, 0, sizeof(*pr_stfs)); |
512 |
pr_stfs->f_type = stfs->f_type; |
513 |
pr_stfs->f_bsize = stfs->f_bsize; |
514 |
pr_stfs->f_blocks = stfs->f_blocks; |
515 |
pr_stfs->f_bfree = stfs->f_bfree; |
516 |
pr_stfs->f_bavail = stfs->f_bavail; |
517 |
pr_stfs->f_files = stfs->f_files; |
518 |
pr_stfs->f_ffree = stfs->f_ffree; |
519 |
pr_stfs->f_fsid[0] = stfs->f_fsid.__val[0]; |
520 |
pr_stfs->f_fsid[1] = stfs->f_fsid.__val[1]; |
521 |
pr_stfs->f_namelen = stfs->f_namelen; |
522 |
pr_stfs->f_frsize = stfs->f_frsize; |
523 |
} |
524 |
|
525 |
/*
|
526 |
* Gets stat/statfs information and packs in out_iovec structure
|
527 |
* on success returns number of bytes packed in out_iovec struture
|
528 |
* otherwise returns -errno
|
529 |
*/
|
530 |
static int do_stat(int type, struct iovec *iovec, struct iovec *out_iovec) |
531 |
{ |
532 |
int retval;
|
533 |
V9fsString path; |
534 |
ProxyStat pr_stat; |
535 |
ProxyStatFS pr_stfs; |
536 |
struct stat st_buf;
|
537 |
struct statfs stfs_buf;
|
538 |
|
539 |
v9fs_string_init(&path); |
540 |
retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "s", &path);
|
541 |
if (retval < 0) { |
542 |
return retval;
|
543 |
} |
544 |
|
545 |
switch (type) {
|
546 |
case T_LSTAT:
|
547 |
retval = lstat(path.data, &st_buf); |
548 |
if (retval < 0) { |
549 |
retval = -errno; |
550 |
} else {
|
551 |
stat_to_prstat(&pr_stat, &st_buf); |
552 |
retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, |
553 |
"qqqdddqqqqqqqqqq", pr_stat.st_dev,
|
554 |
pr_stat.st_ino, pr_stat.st_nlink, |
555 |
pr_stat.st_mode, pr_stat.st_uid, |
556 |
pr_stat.st_gid, pr_stat.st_rdev, |
557 |
pr_stat.st_size, pr_stat.st_blksize, |
558 |
pr_stat.st_blocks, |
559 |
pr_stat.st_atim_sec, pr_stat.st_atim_nsec, |
560 |
pr_stat.st_mtim_sec, pr_stat.st_mtim_nsec, |
561 |
pr_stat.st_ctim_sec, pr_stat.st_ctim_nsec); |
562 |
} |
563 |
break;
|
564 |
case T_STATFS:
|
565 |
retval = statfs(path.data, &stfs_buf); |
566 |
if (retval < 0) { |
567 |
retval = -errno; |
568 |
} else {
|
569 |
statfs_to_prstatfs(&pr_stfs, &stfs_buf); |
570 |
retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, |
571 |
"qqqqqqqqqqq", pr_stfs.f_type,
|
572 |
pr_stfs.f_bsize, pr_stfs.f_blocks, |
573 |
pr_stfs.f_bfree, pr_stfs.f_bavail, |
574 |
pr_stfs.f_files, pr_stfs.f_ffree, |
575 |
pr_stfs.f_fsid[0], pr_stfs.f_fsid[1], |
576 |
pr_stfs.f_namelen, pr_stfs.f_frsize); |
577 |
} |
578 |
break;
|
579 |
} |
580 |
v9fs_string_free(&path); |
581 |
return retval;
|
582 |
} |
583 |
|
584 |
static int do_readlink(struct iovec *iovec, struct iovec *out_iovec) |
585 |
{ |
586 |
char *buffer;
|
587 |
int size, retval;
|
588 |
V9fsString target, path; |
589 |
|
590 |
v9fs_string_init(&path); |
591 |
retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "sd", &path, &size);
|
592 |
if (retval < 0) { |
593 |
v9fs_string_free(&path); |
594 |
return retval;
|
595 |
} |
596 |
buffer = g_malloc(size); |
597 |
v9fs_string_init(&target); |
598 |
retval = readlink(path.data, buffer, size); |
599 |
if (retval > 0) { |
600 |
buffer[retval] = '\0';
|
601 |
v9fs_string_sprintf(&target, "%s", buffer);
|
602 |
retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "s", &target);
|
603 |
} else {
|
604 |
retval = -errno; |
605 |
} |
606 |
g_free(buffer); |
607 |
v9fs_string_free(&target); |
608 |
v9fs_string_free(&path); |
609 |
return retval;
|
610 |
} |
611 |
|
612 |
/*
|
613 |
* create other filesystem objects and send 0 on success
|
614 |
* return -errno on error
|
615 |
*/
|
616 |
static int do_create_others(int type, struct iovec *iovec) |
617 |
{ |
618 |
dev_t rdev; |
619 |
int retval = 0; |
620 |
int offset = PROXY_HDR_SZ;
|
621 |
V9fsString oldpath, path; |
622 |
int mode, uid, gid, cur_uid, cur_gid;
|
623 |
|
624 |
v9fs_string_init(&path); |
625 |
v9fs_string_init(&oldpath); |
626 |
|
627 |
retval = proxy_unmarshal(iovec, offset, "dd", &uid, &gid);
|
628 |
if (retval < 0) { |
629 |
return retval;
|
630 |
} |
631 |
offset += retval; |
632 |
retval = setugid(uid, gid, &cur_uid, &cur_gid); |
633 |
if (retval < 0) { |
634 |
goto unmarshal_err_out;
|
635 |
} |
636 |
switch (type) {
|
637 |
case T_MKNOD:
|
638 |
retval = proxy_unmarshal(iovec, offset, "sdq", &path, &mode, &rdev);
|
639 |
if (retval < 0) { |
640 |
goto err_out;
|
641 |
} |
642 |
retval = mknod(path.data, mode, rdev); |
643 |
break;
|
644 |
case T_MKDIR:
|
645 |
retval = proxy_unmarshal(iovec, offset, "sd", &path, &mode);
|
646 |
if (retval < 0) { |
647 |
goto err_out;
|
648 |
} |
649 |
retval = mkdir(path.data, mode); |
650 |
break;
|
651 |
case T_SYMLINK:
|
652 |
retval = proxy_unmarshal(iovec, offset, "ss", &oldpath, &path);
|
653 |
if (retval < 0) { |
654 |
goto err_out;
|
655 |
} |
656 |
retval = symlink(oldpath.data, path.data); |
657 |
break;
|
658 |
} |
659 |
if (retval < 0) { |
660 |
retval = -errno; |
661 |
} |
662 |
|
663 |
err_out:
|
664 |
resetugid(cur_uid, cur_gid); |
665 |
unmarshal_err_out:
|
666 |
v9fs_string_free(&path); |
667 |
v9fs_string_free(&oldpath); |
668 |
return retval;
|
669 |
} |
670 |
|
671 |
/*
|
672 |
* create a file and send fd on success
|
673 |
* return -errno on error
|
674 |
*/
|
675 |
static int do_create(struct iovec *iovec) |
676 |
{ |
677 |
int ret;
|
678 |
V9fsString path; |
679 |
int flags, mode, uid, gid, cur_uid, cur_gid;
|
680 |
|
681 |
v9fs_string_init(&path); |
682 |
ret = proxy_unmarshal(iovec, PROXY_HDR_SZ, "sdddd",
|
683 |
&path, &flags, &mode, &uid, &gid); |
684 |
if (ret < 0) { |
685 |
goto unmarshal_err_out;
|
686 |
} |
687 |
ret = setugid(uid, gid, &cur_uid, &cur_gid); |
688 |
if (ret < 0) { |
689 |
goto unmarshal_err_out;
|
690 |
} |
691 |
ret = open(path.data, flags, mode); |
692 |
if (ret < 0) { |
693 |
ret = -errno; |
694 |
} |
695 |
|
696 |
resetugid(cur_uid, cur_gid); |
697 |
unmarshal_err_out:
|
698 |
v9fs_string_free(&path); |
699 |
return ret;
|
700 |
} |
701 |
|
702 |
/*
|
703 |
* open a file and send fd on success
|
704 |
* return -errno on error
|
705 |
*/
|
706 |
static int do_open(struct iovec *iovec) |
707 |
{ |
708 |
int flags, ret;
|
709 |
V9fsString path; |
710 |
|
711 |
v9fs_string_init(&path); |
712 |
ret = proxy_unmarshal(iovec, PROXY_HDR_SZ, "sd", &path, &flags);
|
713 |
if (ret < 0) { |
714 |
goto err_out;
|
715 |
} |
716 |
ret = open(path.data, flags); |
717 |
if (ret < 0) { |
718 |
ret = -errno; |
719 |
} |
720 |
err_out:
|
721 |
v9fs_string_free(&path); |
722 |
return ret;
|
723 |
} |
724 |
|
725 |
/* create unix domain socket and return the descriptor */
|
726 |
static int proxy_socket(const char *path, uid_t uid, gid_t gid) |
727 |
{ |
728 |
int sock, client;
|
729 |
struct sockaddr_un proxy, qemu;
|
730 |
socklen_t size; |
731 |
|
732 |
/* requested socket already exists, refuse to start */
|
733 |
if (!access(path, F_OK)) {
|
734 |
do_log(LOG_CRIT, "socket already exists\n");
|
735 |
return -1; |
736 |
} |
737 |
|
738 |
sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
739 |
if (sock < 0) { |
740 |
do_perror("socket");
|
741 |
return -1; |
742 |
} |
743 |
|
744 |
/* mask other part of mode bits */
|
745 |
umask(7);
|
746 |
|
747 |
proxy.sun_family = AF_UNIX; |
748 |
strcpy(proxy.sun_path, path); |
749 |
if (bind(sock, (struct sockaddr *)&proxy, |
750 |
sizeof(struct sockaddr_un)) < 0) { |
751 |
do_perror("bind");
|
752 |
return -1; |
753 |
} |
754 |
if (chown(proxy.sun_path, uid, gid) < 0) { |
755 |
do_perror("chown");
|
756 |
return -1; |
757 |
} |
758 |
if (listen(sock, 1) < 0) { |
759 |
do_perror("listen");
|
760 |
return -1; |
761 |
} |
762 |
|
763 |
client = accept(sock, (struct sockaddr *)&qemu, &size);
|
764 |
if (client < 0) { |
765 |
do_perror("accept");
|
766 |
return -1; |
767 |
} |
768 |
return client;
|
769 |
} |
770 |
|
771 |
static void usage(char *prog) |
772 |
{ |
773 |
fprintf(stderr, "usage: %s\n"
|
774 |
" -p|--path <path> 9p path to export\n"
|
775 |
" {-f|--fd <socket-descriptor>} socket file descriptor to be used\n"
|
776 |
" {-s|--socket <socketname> socket file used for communication\n"
|
777 |
" \t-u|--uid <uid> -g|--gid <gid>} - uid:gid combination to give "
|
778 |
" access to this socket\n"
|
779 |
" \tNote: -s & -f can not be used together\n"
|
780 |
" [-n|--nodaemon] Run as a normal program\n",
|
781 |
basename(prog)); |
782 |
} |
783 |
|
784 |
static int process_reply(int sock, int type, |
785 |
struct iovec *out_iovec, int retval) |
786 |
{ |
787 |
switch (type) {
|
788 |
case T_OPEN:
|
789 |
case T_CREATE:
|
790 |
if (send_fd(sock, retval) < 0) { |
791 |
return -1; |
792 |
} |
793 |
break;
|
794 |
case T_MKNOD:
|
795 |
case T_MKDIR:
|
796 |
case T_SYMLINK:
|
797 |
case T_LINK:
|
798 |
case T_CHMOD:
|
799 |
case T_CHOWN:
|
800 |
case T_TRUNCATE:
|
801 |
case T_UTIME:
|
802 |
case T_RENAME:
|
803 |
case T_REMOVE:
|
804 |
case T_LSETXATTR:
|
805 |
case T_LREMOVEXATTR:
|
806 |
if (send_status(sock, out_iovec, retval) < 0) { |
807 |
return -1; |
808 |
} |
809 |
break;
|
810 |
case T_LSTAT:
|
811 |
case T_STATFS:
|
812 |
case T_READLINK:
|
813 |
case T_LGETXATTR:
|
814 |
case T_LLISTXATTR:
|
815 |
case T_GETVERSION:
|
816 |
if (send_response(sock, out_iovec, retval) < 0) { |
817 |
return -1; |
818 |
} |
819 |
break;
|
820 |
default:
|
821 |
return -1; |
822 |
break;
|
823 |
} |
824 |
return 0; |
825 |
} |
826 |
|
827 |
static int process_requests(int sock) |
828 |
{ |
829 |
int flags;
|
830 |
int size = 0; |
831 |
int retval = 0; |
832 |
uint64_t offset; |
833 |
ProxyHeader header; |
834 |
int mode, uid, gid;
|
835 |
V9fsString name, value; |
836 |
struct timespec spec[2]; |
837 |
V9fsString oldpath, path; |
838 |
struct iovec in_iovec, out_iovec;
|
839 |
|
840 |
in_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ); |
841 |
in_iovec.iov_len = PROXY_MAX_IO_SZ + PROXY_HDR_SZ; |
842 |
out_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ); |
843 |
out_iovec.iov_len = PROXY_MAX_IO_SZ + PROXY_HDR_SZ; |
844 |
|
845 |
while (1) { |
846 |
/*
|
847 |
* initialize the header type, so that we send
|
848 |
* response to proper request type.
|
849 |
*/
|
850 |
header.type = 0;
|
851 |
retval = read_request(sock, &in_iovec, &header); |
852 |
if (retval < 0) { |
853 |
goto err_out;
|
854 |
} |
855 |
|
856 |
switch (header.type) {
|
857 |
case T_OPEN:
|
858 |
retval = do_open(&in_iovec); |
859 |
break;
|
860 |
case T_CREATE:
|
861 |
retval = do_create(&in_iovec); |
862 |
break;
|
863 |
case T_MKNOD:
|
864 |
case T_MKDIR:
|
865 |
case T_SYMLINK:
|
866 |
retval = do_create_others(header.type, &in_iovec); |
867 |
break;
|
868 |
case T_LINK:
|
869 |
v9fs_string_init(&path); |
870 |
v9fs_string_init(&oldpath); |
871 |
retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, |
872 |
"ss", &oldpath, &path);
|
873 |
if (retval > 0) { |
874 |
retval = link(oldpath.data, path.data); |
875 |
if (retval < 0) { |
876 |
retval = -errno; |
877 |
} |
878 |
} |
879 |
v9fs_string_free(&oldpath); |
880 |
v9fs_string_free(&path); |
881 |
break;
|
882 |
case T_LSTAT:
|
883 |
case T_STATFS:
|
884 |
retval = do_stat(header.type, &in_iovec, &out_iovec); |
885 |
break;
|
886 |
case T_READLINK:
|
887 |
retval = do_readlink(&in_iovec, &out_iovec); |
888 |
break;
|
889 |
case T_CHMOD:
|
890 |
v9fs_string_init(&path); |
891 |
retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, |
892 |
"sd", &path, &mode);
|
893 |
if (retval > 0) { |
894 |
retval = chmod(path.data, mode); |
895 |
if (retval < 0) { |
896 |
retval = -errno; |
897 |
} |
898 |
} |
899 |
v9fs_string_free(&path); |
900 |
break;
|
901 |
case T_CHOWN:
|
902 |
v9fs_string_init(&path); |
903 |
retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sdd", &path,
|
904 |
&uid, &gid); |
905 |
if (retval > 0) { |
906 |
retval = lchown(path.data, uid, gid); |
907 |
if (retval < 0) { |
908 |
retval = -errno; |
909 |
} |
910 |
} |
911 |
v9fs_string_free(&path); |
912 |
break;
|
913 |
case T_TRUNCATE:
|
914 |
v9fs_string_init(&path); |
915 |
retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sq",
|
916 |
&path, &offset); |
917 |
if (retval > 0) { |
918 |
retval = truncate(path.data, offset); |
919 |
if (retval < 0) { |
920 |
retval = -errno; |
921 |
} |
922 |
} |
923 |
v9fs_string_free(&path); |
924 |
break;
|
925 |
case T_UTIME:
|
926 |
v9fs_string_init(&path); |
927 |
retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sqqqq", &path,
|
928 |
&spec[0].tv_sec, &spec[0].tv_nsec, |
929 |
&spec[1].tv_sec, &spec[1].tv_nsec); |
930 |
if (retval > 0) { |
931 |
retval = qemu_utimens(path.data, spec); |
932 |
if (retval < 0) { |
933 |
retval = -errno; |
934 |
} |
935 |
} |
936 |
v9fs_string_free(&path); |
937 |
break;
|
938 |
case T_RENAME:
|
939 |
v9fs_string_init(&path); |
940 |
v9fs_string_init(&oldpath); |
941 |
retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, |
942 |
"ss", &oldpath, &path);
|
943 |
if (retval > 0) { |
944 |
retval = rename(oldpath.data, path.data); |
945 |
if (retval < 0) { |
946 |
retval = -errno; |
947 |
} |
948 |
} |
949 |
v9fs_string_free(&oldpath); |
950 |
v9fs_string_free(&path); |
951 |
break;
|
952 |
case T_REMOVE:
|
953 |
v9fs_string_init(&path); |
954 |
retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "s", &path);
|
955 |
if (retval > 0) { |
956 |
retval = remove(path.data); |
957 |
if (retval < 0) { |
958 |
retval = -errno; |
959 |
} |
960 |
} |
961 |
v9fs_string_free(&path); |
962 |
break;
|
963 |
case T_LGETXATTR:
|
964 |
case T_LLISTXATTR:
|
965 |
retval = do_getxattr(header.type, &in_iovec, &out_iovec); |
966 |
break;
|
967 |
case T_LSETXATTR:
|
968 |
v9fs_string_init(&path); |
969 |
v9fs_string_init(&name); |
970 |
v9fs_string_init(&value); |
971 |
retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sssdd", &path,
|
972 |
&name, &value, &size, &flags); |
973 |
if (retval > 0) { |
974 |
retval = lsetxattr(path.data, |
975 |
name.data, value.data, size, flags); |
976 |
if (retval < 0) { |
977 |
retval = -errno; |
978 |
} |
979 |
} |
980 |
v9fs_string_free(&path); |
981 |
v9fs_string_free(&name); |
982 |
v9fs_string_free(&value); |
983 |
break;
|
984 |
case T_LREMOVEXATTR:
|
985 |
v9fs_string_init(&path); |
986 |
v9fs_string_init(&name); |
987 |
retval = proxy_unmarshal(&in_iovec, |
988 |
PROXY_HDR_SZ, "ss", &path, &name);
|
989 |
if (retval > 0) { |
990 |
retval = lremovexattr(path.data, name.data); |
991 |
if (retval < 0) { |
992 |
retval = -errno; |
993 |
} |
994 |
} |
995 |
v9fs_string_free(&path); |
996 |
v9fs_string_free(&name); |
997 |
break;
|
998 |
case T_GETVERSION:
|
999 |
retval = do_getversion(&in_iovec, &out_iovec); |
1000 |
break;
|
1001 |
default:
|
1002 |
goto err_out;
|
1003 |
break;
|
1004 |
} |
1005 |
|
1006 |
if (process_reply(sock, header.type, &out_iovec, retval) < 0) { |
1007 |
goto err_out;
|
1008 |
} |
1009 |
} |
1010 |
err_out:
|
1011 |
g_free(in_iovec.iov_base); |
1012 |
g_free(out_iovec.iov_base); |
1013 |
return -1; |
1014 |
} |
1015 |
|
1016 |
int main(int argc, char **argv) |
1017 |
{ |
1018 |
int sock;
|
1019 |
uid_t own_u; |
1020 |
gid_t own_g; |
1021 |
char *rpath = NULL; |
1022 |
char *sock_name = NULL; |
1023 |
struct stat stbuf;
|
1024 |
int c, option_index;
|
1025 |
#ifdef FS_IOC_GETVERSION
|
1026 |
int retval;
|
1027 |
struct statfs st_fs;
|
1028 |
#endif
|
1029 |
|
1030 |
is_daemon = true;
|
1031 |
sock = -1;
|
1032 |
own_u = own_g = -1;
|
1033 |
while (1) { |
1034 |
option_index = 0;
|
1035 |
c = getopt_long(argc, argv, "p:nh?f:s:u:g:", helper_opts,
|
1036 |
&option_index); |
1037 |
if (c == -1) { |
1038 |
break;
|
1039 |
} |
1040 |
switch (c) {
|
1041 |
case 'p': |
1042 |
rpath = strdup(optarg); |
1043 |
break;
|
1044 |
case 'n': |
1045 |
is_daemon = false;
|
1046 |
break;
|
1047 |
case 'f': |
1048 |
sock = atoi(optarg); |
1049 |
break;
|
1050 |
case 's': |
1051 |
sock_name = strdup(optarg); |
1052 |
break;
|
1053 |
case 'u': |
1054 |
own_u = atoi(optarg); |
1055 |
break;
|
1056 |
case 'g': |
1057 |
own_g = atoi(optarg); |
1058 |
break;
|
1059 |
case '?': |
1060 |
case 'h': |
1061 |
default:
|
1062 |
usage(argv[0]);
|
1063 |
exit(EXIT_FAILURE); |
1064 |
} |
1065 |
} |
1066 |
|
1067 |
/* Parameter validation */
|
1068 |
if ((sock_name == NULL && sock == -1) || rpath == NULL) { |
1069 |
fprintf(stderr, "socket, socket descriptor or path not specified\n");
|
1070 |
usage(argv[0]);
|
1071 |
return -1; |
1072 |
} |
1073 |
|
1074 |
if (sock_name && sock != -1) { |
1075 |
fprintf(stderr, "both named socket and socket descriptor specified\n");
|
1076 |
usage(argv[0]);
|
1077 |
exit(EXIT_FAILURE); |
1078 |
} |
1079 |
|
1080 |
if (sock_name && (own_u == -1 || own_g == -1)) { |
1081 |
fprintf(stderr, "owner uid:gid not specified, ");
|
1082 |
fprintf(stderr, |
1083 |
"owner uid:gid specifies who can access the socket file\n");
|
1084 |
usage(argv[0]);
|
1085 |
exit(EXIT_FAILURE); |
1086 |
} |
1087 |
|
1088 |
if (lstat(rpath, &stbuf) < 0) { |
1089 |
fprintf(stderr, "invalid path \"%s\" specified, %s\n",
|
1090 |
rpath, strerror(errno)); |
1091 |
exit(EXIT_FAILURE); |
1092 |
} |
1093 |
|
1094 |
if (!S_ISDIR(stbuf.st_mode)) {
|
1095 |
fprintf(stderr, "specified path \"%s\" is not directory\n", rpath);
|
1096 |
exit(EXIT_FAILURE); |
1097 |
} |
1098 |
|
1099 |
if (is_daemon) {
|
1100 |
if (daemon(0, 0) < 0) { |
1101 |
fprintf(stderr, "daemon call failed\n");
|
1102 |
exit(EXIT_FAILURE); |
1103 |
} |
1104 |
openlog(PROGNAME, LOG_PID, LOG_DAEMON); |
1105 |
} |
1106 |
|
1107 |
do_log(LOG_INFO, "Started\n");
|
1108 |
if (sock_name) {
|
1109 |
sock = proxy_socket(sock_name, own_u, own_g); |
1110 |
if (sock < 0) { |
1111 |
goto error;
|
1112 |
} |
1113 |
} |
1114 |
|
1115 |
get_version = false;
|
1116 |
#ifdef FS_IOC_GETVERSION
|
1117 |
/* check whether underlying FS support IOC_GETVERSION */
|
1118 |
retval = statfs(rpath, &st_fs); |
1119 |
if (!retval) {
|
1120 |
switch (st_fs.f_type) {
|
1121 |
case EXT2_SUPER_MAGIC:
|
1122 |
case BTRFS_SUPER_MAGIC:
|
1123 |
case REISERFS_SUPER_MAGIC:
|
1124 |
case XFS_SUPER_MAGIC:
|
1125 |
get_version = true;
|
1126 |
break;
|
1127 |
} |
1128 |
} |
1129 |
#endif
|
1130 |
|
1131 |
if (chdir("/") < 0) { |
1132 |
do_perror("chdir");
|
1133 |
goto error;
|
1134 |
} |
1135 |
if (chroot(rpath) < 0) { |
1136 |
do_perror("chroot");
|
1137 |
goto error;
|
1138 |
} |
1139 |
umask(0);
|
1140 |
|
1141 |
if (init_capabilities() < 0) { |
1142 |
goto error;
|
1143 |
} |
1144 |
|
1145 |
process_requests(sock); |
1146 |
error:
|
1147 |
do_log(LOG_INFO, "Done\n");
|
1148 |
closelog(); |
1149 |
return 0; |
1150 |
} |