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