root / fsdev / virtfs-proxy-helper.c @ b072a3c8
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 |
|
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_socket.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 |
* 4. If the file system user ID is changed from 0 to nonzero (see setfsuid(2))
|
276 |
* then the following capabilities are cleared from the effective set:
|
277 |
* CAP_CHOWN, CAP_DAC_OVERRIDE, CAP_DAC_READ_SEARCH, CAP_FOWNER, CAP_FSETID,
|
278 |
* CAP_LINUX_IMMUTABLE (since Linux 2.2.30), CAP_MAC_OVERRIDE, and CAP_MKNOD
|
279 |
* (since Linux 2.2.30). If the file system UID is changed from nonzero to 0,
|
280 |
* then any of these capabilities that are enabled in the permitted set
|
281 |
* are enabled in the effective set.
|
282 |
*/
|
283 |
static int setfsugid(int uid, int gid) |
284 |
{ |
285 |
/*
|
286 |
* We still need DAC_OVERRIDE because we don't change
|
287 |
* supplementary group ids, and hence may be subjected DAC rules
|
288 |
*/
|
289 |
cap_value_t cap_list[] = { |
290 |
CAP_DAC_OVERRIDE, |
291 |
}; |
292 |
|
293 |
setfsgid(gid); |
294 |
setfsuid(uid); |
295 |
|
296 |
if (uid != 0 || gid != 0) { |
297 |
return do_cap_set(cap_list, ARRAY_SIZE(cap_list), 0); |
298 |
} |
299 |
return 0; |
300 |
} |
301 |
|
302 |
/*
|
303 |
* send response in two parts
|
304 |
* 1) ProxyHeader
|
305 |
* 2) Response or error status
|
306 |
* This function should be called with marshaled response
|
307 |
* send_response constructs header part and error part only.
|
308 |
* send response sends {ProxyHeader,Response} if the request was success
|
309 |
* otherwise sends {ProxyHeader,error status}
|
310 |
*/
|
311 |
static int send_response(int sock, struct iovec *iovec, int size) |
312 |
{ |
313 |
int retval;
|
314 |
ProxyHeader header; |
315 |
|
316 |
/*
|
317 |
* If response size exceeds available iovec->iov_len,
|
318 |
* we return ENOBUFS
|
319 |
*/
|
320 |
if (size > PROXY_MAX_IO_SZ) {
|
321 |
size = -ENOBUFS; |
322 |
} |
323 |
|
324 |
if (size < 0) { |
325 |
/*
|
326 |
* In case of error we would not have got the error encoded
|
327 |
* already so encode the error here.
|
328 |
*/
|
329 |
header.type = T_ERROR; |
330 |
header.size = sizeof(size);
|
331 |
proxy_marshal(iovec, PROXY_HDR_SZ, "d", size);
|
332 |
} else {
|
333 |
header.type = T_SUCCESS; |
334 |
header.size = size; |
335 |
} |
336 |
proxy_marshal(iovec, 0, "dd", header.type, header.size); |
337 |
retval = socket_write(sock, iovec->iov_base, header.size + PROXY_HDR_SZ); |
338 |
if (retval < 0) { |
339 |
return retval;;
|
340 |
} |
341 |
return 0; |
342 |
} |
343 |
|
344 |
/*
|
345 |
* gets generation number
|
346 |
* returns -errno on failure and sizeof(generation number) on success
|
347 |
*/
|
348 |
static int do_getversion(struct iovec *iovec, struct iovec *out_iovec) |
349 |
{ |
350 |
uint64_t version; |
351 |
int retval = -ENOTTY;
|
352 |
#ifdef FS_IOC_GETVERSION
|
353 |
int fd;
|
354 |
V9fsString path; |
355 |
#endif
|
356 |
|
357 |
|
358 |
/* no need to issue ioctl */
|
359 |
if (!get_version) {
|
360 |
version = 0;
|
361 |
retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "q", version);
|
362 |
return retval;
|
363 |
} |
364 |
#ifdef FS_IOC_GETVERSION
|
365 |
retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "s", &path);
|
366 |
if (retval < 0) { |
367 |
return retval;
|
368 |
} |
369 |
|
370 |
fd = open(path.data, O_RDONLY); |
371 |
if (fd < 0) { |
372 |
retval = -errno; |
373 |
goto err_out;
|
374 |
} |
375 |
if (ioctl(fd, FS_IOC_GETVERSION, &version) < 0) { |
376 |
retval = -errno; |
377 |
} else {
|
378 |
retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "q", version);
|
379 |
} |
380 |
close(fd); |
381 |
err_out:
|
382 |
v9fs_string_free(&path); |
383 |
#endif
|
384 |
return retval;
|
385 |
} |
386 |
|
387 |
static int do_getxattr(int type, struct iovec *iovec, struct iovec *out_iovec) |
388 |
{ |
389 |
int size = 0, offset, retval; |
390 |
V9fsString path, name, xattr; |
391 |
|
392 |
v9fs_string_init(&xattr); |
393 |
v9fs_string_init(&path); |
394 |
retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "ds", &size, &path);
|
395 |
if (retval < 0) { |
396 |
return retval;
|
397 |
} |
398 |
offset = PROXY_HDR_SZ + retval; |
399 |
|
400 |
if (size) {
|
401 |
xattr.data = g_malloc(size); |
402 |
xattr.size = size; |
403 |
} |
404 |
switch (type) {
|
405 |
case T_LGETXATTR:
|
406 |
v9fs_string_init(&name); |
407 |
retval = proxy_unmarshal(iovec, offset, "s", &name);
|
408 |
if (retval > 0) { |
409 |
retval = lgetxattr(path.data, name.data, xattr.data, size); |
410 |
if (retval < 0) { |
411 |
retval = -errno; |
412 |
} else {
|
413 |
xattr.size = retval; |
414 |
} |
415 |
} |
416 |
v9fs_string_free(&name); |
417 |
break;
|
418 |
case T_LLISTXATTR:
|
419 |
retval = llistxattr(path.data, xattr.data, size); |
420 |
if (retval < 0) { |
421 |
retval = -errno; |
422 |
} else {
|
423 |
xattr.size = retval; |
424 |
} |
425 |
break;
|
426 |
} |
427 |
if (retval < 0) { |
428 |
goto err_out;
|
429 |
} |
430 |
|
431 |
if (!size) {
|
432 |
proxy_marshal(out_iovec, PROXY_HDR_SZ, "d", retval);
|
433 |
retval = sizeof(retval);
|
434 |
} else {
|
435 |
retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "s", &xattr);
|
436 |
} |
437 |
err_out:
|
438 |
v9fs_string_free(&xattr); |
439 |
v9fs_string_free(&path); |
440 |
return retval;
|
441 |
} |
442 |
|
443 |
static void stat_to_prstat(ProxyStat *pr_stat, struct stat *stat) |
444 |
{ |
445 |
memset(pr_stat, 0, sizeof(*pr_stat)); |
446 |
pr_stat->st_dev = stat->st_dev; |
447 |
pr_stat->st_ino = stat->st_ino; |
448 |
pr_stat->st_nlink = stat->st_nlink; |
449 |
pr_stat->st_mode = stat->st_mode; |
450 |
pr_stat->st_uid = stat->st_uid; |
451 |
pr_stat->st_gid = stat->st_gid; |
452 |
pr_stat->st_rdev = stat->st_rdev; |
453 |
pr_stat->st_size = stat->st_size; |
454 |
pr_stat->st_blksize = stat->st_blksize; |
455 |
pr_stat->st_blocks = stat->st_blocks; |
456 |
pr_stat->st_atim_sec = stat->st_atim.tv_sec; |
457 |
pr_stat->st_atim_nsec = stat->st_atim.tv_nsec; |
458 |
pr_stat->st_mtim_sec = stat->st_mtim.tv_sec; |
459 |
pr_stat->st_mtim_nsec = stat->st_mtim.tv_nsec; |
460 |
pr_stat->st_ctim_sec = stat->st_ctim.tv_sec; |
461 |
pr_stat->st_ctim_nsec = stat->st_ctim.tv_nsec; |
462 |
} |
463 |
|
464 |
static void statfs_to_prstatfs(ProxyStatFS *pr_stfs, struct statfs *stfs) |
465 |
{ |
466 |
memset(pr_stfs, 0, sizeof(*pr_stfs)); |
467 |
pr_stfs->f_type = stfs->f_type; |
468 |
pr_stfs->f_bsize = stfs->f_bsize; |
469 |
pr_stfs->f_blocks = stfs->f_blocks; |
470 |
pr_stfs->f_bfree = stfs->f_bfree; |
471 |
pr_stfs->f_bavail = stfs->f_bavail; |
472 |
pr_stfs->f_files = stfs->f_files; |
473 |
pr_stfs->f_ffree = stfs->f_ffree; |
474 |
pr_stfs->f_fsid[0] = stfs->f_fsid.__val[0]; |
475 |
pr_stfs->f_fsid[1] = stfs->f_fsid.__val[1]; |
476 |
pr_stfs->f_namelen = stfs->f_namelen; |
477 |
pr_stfs->f_frsize = stfs->f_frsize; |
478 |
} |
479 |
|
480 |
/*
|
481 |
* Gets stat/statfs information and packs in out_iovec structure
|
482 |
* on success returns number of bytes packed in out_iovec struture
|
483 |
* otherwise returns -errno
|
484 |
*/
|
485 |
static int do_stat(int type, struct iovec *iovec, struct iovec *out_iovec) |
486 |
{ |
487 |
int retval;
|
488 |
V9fsString path; |
489 |
ProxyStat pr_stat; |
490 |
ProxyStatFS pr_stfs; |
491 |
struct stat st_buf;
|
492 |
struct statfs stfs_buf;
|
493 |
|
494 |
v9fs_string_init(&path); |
495 |
retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "s", &path);
|
496 |
if (retval < 0) { |
497 |
return retval;
|
498 |
} |
499 |
|
500 |
switch (type) {
|
501 |
case T_LSTAT:
|
502 |
retval = lstat(path.data, &st_buf); |
503 |
if (retval < 0) { |
504 |
retval = -errno; |
505 |
} else {
|
506 |
stat_to_prstat(&pr_stat, &st_buf); |
507 |
retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, |
508 |
"qqqdddqqqqqqqqqq", pr_stat.st_dev,
|
509 |
pr_stat.st_ino, pr_stat.st_nlink, |
510 |
pr_stat.st_mode, pr_stat.st_uid, |
511 |
pr_stat.st_gid, pr_stat.st_rdev, |
512 |
pr_stat.st_size, pr_stat.st_blksize, |
513 |
pr_stat.st_blocks, |
514 |
pr_stat.st_atim_sec, pr_stat.st_atim_nsec, |
515 |
pr_stat.st_mtim_sec, pr_stat.st_mtim_nsec, |
516 |
pr_stat.st_ctim_sec, pr_stat.st_ctim_nsec); |
517 |
} |
518 |
break;
|
519 |
case T_STATFS:
|
520 |
retval = statfs(path.data, &stfs_buf); |
521 |
if (retval < 0) { |
522 |
retval = -errno; |
523 |
} else {
|
524 |
statfs_to_prstatfs(&pr_stfs, &stfs_buf); |
525 |
retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, |
526 |
"qqqqqqqqqqq", pr_stfs.f_type,
|
527 |
pr_stfs.f_bsize, pr_stfs.f_blocks, |
528 |
pr_stfs.f_bfree, pr_stfs.f_bavail, |
529 |
pr_stfs.f_files, pr_stfs.f_ffree, |
530 |
pr_stfs.f_fsid[0], pr_stfs.f_fsid[1], |
531 |
pr_stfs.f_namelen, pr_stfs.f_frsize); |
532 |
} |
533 |
break;
|
534 |
} |
535 |
v9fs_string_free(&path); |
536 |
return retval;
|
537 |
} |
538 |
|
539 |
static int do_readlink(struct iovec *iovec, struct iovec *out_iovec) |
540 |
{ |
541 |
char *buffer;
|
542 |
int size, retval;
|
543 |
V9fsString target, path; |
544 |
|
545 |
v9fs_string_init(&path); |
546 |
retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "sd", &path, &size);
|
547 |
if (retval < 0) { |
548 |
v9fs_string_free(&path); |
549 |
return retval;
|
550 |
} |
551 |
buffer = g_malloc(size); |
552 |
v9fs_string_init(&target); |
553 |
retval = readlink(path.data, buffer, size); |
554 |
if (retval > 0) { |
555 |
buffer[retval] = '\0';
|
556 |
v9fs_string_sprintf(&target, "%s", buffer);
|
557 |
retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "s", &target);
|
558 |
} else {
|
559 |
retval = -errno; |
560 |
} |
561 |
g_free(buffer); |
562 |
v9fs_string_free(&target); |
563 |
v9fs_string_free(&path); |
564 |
return retval;
|
565 |
} |
566 |
|
567 |
/*
|
568 |
* create other filesystem objects and send 0 on success
|
569 |
* return -errno on error
|
570 |
*/
|
571 |
static int do_create_others(int type, struct iovec *iovec) |
572 |
{ |
573 |
dev_t rdev; |
574 |
int retval = 0; |
575 |
int offset = PROXY_HDR_SZ;
|
576 |
V9fsString oldpath, path; |
577 |
int mode, uid, gid, cur_uid, cur_gid;
|
578 |
|
579 |
v9fs_string_init(&path); |
580 |
v9fs_string_init(&oldpath); |
581 |
cur_uid = geteuid(); |
582 |
cur_gid = getegid(); |
583 |
|
584 |
retval = proxy_unmarshal(iovec, offset, "dd", &uid, &gid);
|
585 |
if (retval < 0) { |
586 |
return retval;
|
587 |
} |
588 |
offset += retval; |
589 |
retval = setfsugid(uid, gid); |
590 |
if (retval < 0) { |
591 |
retval = -errno; |
592 |
goto err_out;
|
593 |
} |
594 |
switch (type) {
|
595 |
case T_MKNOD:
|
596 |
retval = proxy_unmarshal(iovec, offset, "sdq", &path, &mode, &rdev);
|
597 |
if (retval < 0) { |
598 |
goto err_out;
|
599 |
} |
600 |
retval = mknod(path.data, mode, rdev); |
601 |
break;
|
602 |
case T_MKDIR:
|
603 |
retval = proxy_unmarshal(iovec, offset, "sd", &path, &mode);
|
604 |
if (retval < 0) { |
605 |
goto err_out;
|
606 |
} |
607 |
retval = mkdir(path.data, mode); |
608 |
break;
|
609 |
case T_SYMLINK:
|
610 |
retval = proxy_unmarshal(iovec, offset, "ss", &oldpath, &path);
|
611 |
if (retval < 0) { |
612 |
goto err_out;
|
613 |
} |
614 |
retval = symlink(oldpath.data, path.data); |
615 |
break;
|
616 |
} |
617 |
if (retval < 0) { |
618 |
retval = -errno; |
619 |
} |
620 |
|
621 |
err_out:
|
622 |
v9fs_string_free(&path); |
623 |
v9fs_string_free(&oldpath); |
624 |
setfsugid(cur_uid, cur_gid); |
625 |
return retval;
|
626 |
} |
627 |
|
628 |
/*
|
629 |
* create a file and send fd on success
|
630 |
* return -errno on error
|
631 |
*/
|
632 |
static int do_create(struct iovec *iovec) |
633 |
{ |
634 |
int ret;
|
635 |
V9fsString path; |
636 |
int flags, mode, uid, gid, cur_uid, cur_gid;
|
637 |
|
638 |
v9fs_string_init(&path); |
639 |
ret = proxy_unmarshal(iovec, PROXY_HDR_SZ, "sdddd",
|
640 |
&path, &flags, &mode, &uid, &gid); |
641 |
if (ret < 0) { |
642 |
goto unmarshal_err_out;
|
643 |
} |
644 |
cur_uid = geteuid(); |
645 |
cur_gid = getegid(); |
646 |
ret = setfsugid(uid, gid); |
647 |
if (ret < 0) { |
648 |
/*
|
649 |
* On failure reset back to the
|
650 |
* old uid/gid
|
651 |
*/
|
652 |
ret = -errno; |
653 |
goto err_out;
|
654 |
} |
655 |
ret = open(path.data, flags, mode); |
656 |
if (ret < 0) { |
657 |
ret = -errno; |
658 |
} |
659 |
|
660 |
err_out:
|
661 |
setfsugid(cur_uid, cur_gid); |
662 |
unmarshal_err_out:
|
663 |
v9fs_string_free(&path); |
664 |
return ret;
|
665 |
} |
666 |
|
667 |
/*
|
668 |
* open a file and send fd on success
|
669 |
* return -errno on error
|
670 |
*/
|
671 |
static int do_open(struct iovec *iovec) |
672 |
{ |
673 |
int flags, ret;
|
674 |
V9fsString path; |
675 |
|
676 |
v9fs_string_init(&path); |
677 |
ret = proxy_unmarshal(iovec, PROXY_HDR_SZ, "sd", &path, &flags);
|
678 |
if (ret < 0) { |
679 |
goto err_out;
|
680 |
} |
681 |
ret = open(path.data, flags); |
682 |
if (ret < 0) { |
683 |
ret = -errno; |
684 |
} |
685 |
err_out:
|
686 |
v9fs_string_free(&path); |
687 |
return ret;
|
688 |
} |
689 |
|
690 |
/* create unix domain socket and return the descriptor */
|
691 |
static int proxy_socket(const char *path, uid_t uid, gid_t gid) |
692 |
{ |
693 |
int sock, client;
|
694 |
struct sockaddr_un proxy, qemu;
|
695 |
socklen_t size; |
696 |
|
697 |
/* requested socket already exists, refuse to start */
|
698 |
if (!access(path, F_OK)) {
|
699 |
do_log(LOG_CRIT, "socket already exists\n");
|
700 |
return -1; |
701 |
} |
702 |
|
703 |
sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
704 |
if (sock < 0) { |
705 |
do_perror("socket");
|
706 |
return -1; |
707 |
} |
708 |
|
709 |
/* mask other part of mode bits */
|
710 |
umask(7);
|
711 |
|
712 |
proxy.sun_family = AF_UNIX; |
713 |
strcpy(proxy.sun_path, path); |
714 |
if (bind(sock, (struct sockaddr *)&proxy, |
715 |
sizeof(struct sockaddr_un)) < 0) { |
716 |
do_perror("bind");
|
717 |
return -1; |
718 |
} |
719 |
if (chown(proxy.sun_path, uid, gid) < 0) { |
720 |
do_perror("chown");
|
721 |
return -1; |
722 |
} |
723 |
if (listen(sock, 1) < 0) { |
724 |
do_perror("listen");
|
725 |
return -1; |
726 |
} |
727 |
|
728 |
client = accept(sock, (struct sockaddr *)&qemu, &size);
|
729 |
if (client < 0) { |
730 |
do_perror("accept");
|
731 |
return -1; |
732 |
} |
733 |
return client;
|
734 |
} |
735 |
|
736 |
static void usage(char *prog) |
737 |
{ |
738 |
fprintf(stderr, "usage: %s\n"
|
739 |
" -p|--path <path> 9p path to export\n"
|
740 |
" {-f|--fd <socket-descriptor>} socket file descriptor to be used\n"
|
741 |
" {-s|--socket <socketname> socket file used for communication\n"
|
742 |
" \t-u|--uid <uid> -g|--gid <gid>} - uid:gid combination to give "
|
743 |
" access to this socket\n"
|
744 |
" \tNote: -s & -f can not be used together\n"
|
745 |
" [-n|--nodaemon] Run as a normal program\n",
|
746 |
basename(prog)); |
747 |
} |
748 |
|
749 |
static int process_reply(int sock, int type, |
750 |
struct iovec *out_iovec, int retval) |
751 |
{ |
752 |
switch (type) {
|
753 |
case T_OPEN:
|
754 |
case T_CREATE:
|
755 |
if (send_fd(sock, retval) < 0) { |
756 |
return -1; |
757 |
} |
758 |
break;
|
759 |
case T_MKNOD:
|
760 |
case T_MKDIR:
|
761 |
case T_SYMLINK:
|
762 |
case T_LINK:
|
763 |
case T_CHMOD:
|
764 |
case T_CHOWN:
|
765 |
case T_TRUNCATE:
|
766 |
case T_UTIME:
|
767 |
case T_RENAME:
|
768 |
case T_REMOVE:
|
769 |
case T_LSETXATTR:
|
770 |
case T_LREMOVEXATTR:
|
771 |
if (send_status(sock, out_iovec, retval) < 0) { |
772 |
return -1; |
773 |
} |
774 |
break;
|
775 |
case T_LSTAT:
|
776 |
case T_STATFS:
|
777 |
case T_READLINK:
|
778 |
case T_LGETXATTR:
|
779 |
case T_LLISTXATTR:
|
780 |
case T_GETVERSION:
|
781 |
if (send_response(sock, out_iovec, retval) < 0) { |
782 |
return -1; |
783 |
} |
784 |
break;
|
785 |
default:
|
786 |
return -1; |
787 |
break;
|
788 |
} |
789 |
return 0; |
790 |
} |
791 |
|
792 |
static int process_requests(int sock) |
793 |
{ |
794 |
int flags;
|
795 |
int size = 0; |
796 |
int retval = 0; |
797 |
uint64_t offset; |
798 |
ProxyHeader header; |
799 |
int mode, uid, gid;
|
800 |
V9fsString name, value; |
801 |
struct timespec spec[2]; |
802 |
V9fsString oldpath, path; |
803 |
struct iovec in_iovec, out_iovec;
|
804 |
|
805 |
in_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ); |
806 |
in_iovec.iov_len = PROXY_MAX_IO_SZ + PROXY_HDR_SZ; |
807 |
out_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ); |
808 |
out_iovec.iov_len = PROXY_MAX_IO_SZ + PROXY_HDR_SZ; |
809 |
|
810 |
while (1) { |
811 |
/*
|
812 |
* initialize the header type, so that we send
|
813 |
* response to proper request type.
|
814 |
*/
|
815 |
header.type = 0;
|
816 |
retval = read_request(sock, &in_iovec, &header); |
817 |
if (retval < 0) { |
818 |
goto err_out;
|
819 |
} |
820 |
|
821 |
switch (header.type) {
|
822 |
case T_OPEN:
|
823 |
retval = do_open(&in_iovec); |
824 |
break;
|
825 |
case T_CREATE:
|
826 |
retval = do_create(&in_iovec); |
827 |
break;
|
828 |
case T_MKNOD:
|
829 |
case T_MKDIR:
|
830 |
case T_SYMLINK:
|
831 |
retval = do_create_others(header.type, &in_iovec); |
832 |
break;
|
833 |
case T_LINK:
|
834 |
v9fs_string_init(&path); |
835 |
v9fs_string_init(&oldpath); |
836 |
retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, |
837 |
"ss", &oldpath, &path);
|
838 |
if (retval > 0) { |
839 |
retval = link(oldpath.data, path.data); |
840 |
if (retval < 0) { |
841 |
retval = -errno; |
842 |
} |
843 |
} |
844 |
v9fs_string_free(&oldpath); |
845 |
v9fs_string_free(&path); |
846 |
break;
|
847 |
case T_LSTAT:
|
848 |
case T_STATFS:
|
849 |
retval = do_stat(header.type, &in_iovec, &out_iovec); |
850 |
break;
|
851 |
case T_READLINK:
|
852 |
retval = do_readlink(&in_iovec, &out_iovec); |
853 |
break;
|
854 |
case T_CHMOD:
|
855 |
v9fs_string_init(&path); |
856 |
retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, |
857 |
"sd", &path, &mode);
|
858 |
if (retval > 0) { |
859 |
retval = chmod(path.data, mode); |
860 |
if (retval < 0) { |
861 |
retval = -errno; |
862 |
} |
863 |
} |
864 |
v9fs_string_free(&path); |
865 |
break;
|
866 |
case T_CHOWN:
|
867 |
v9fs_string_init(&path); |
868 |
retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sdd", &path,
|
869 |
&uid, &gid); |
870 |
if (retval > 0) { |
871 |
retval = lchown(path.data, uid, gid); |
872 |
if (retval < 0) { |
873 |
retval = -errno; |
874 |
} |
875 |
} |
876 |
v9fs_string_free(&path); |
877 |
break;
|
878 |
case T_TRUNCATE:
|
879 |
v9fs_string_init(&path); |
880 |
retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sq",
|
881 |
&path, &offset); |
882 |
if (retval > 0) { |
883 |
retval = truncate(path.data, offset); |
884 |
if (retval < 0) { |
885 |
retval = -errno; |
886 |
} |
887 |
} |
888 |
v9fs_string_free(&path); |
889 |
break;
|
890 |
case T_UTIME:
|
891 |
v9fs_string_init(&path); |
892 |
retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sqqqq", &path,
|
893 |
&spec[0].tv_sec, &spec[0].tv_nsec, |
894 |
&spec[1].tv_sec, &spec[1].tv_nsec); |
895 |
if (retval > 0) { |
896 |
retval = qemu_utimens(path.data, spec); |
897 |
if (retval < 0) { |
898 |
retval = -errno; |
899 |
} |
900 |
} |
901 |
v9fs_string_free(&path); |
902 |
break;
|
903 |
case T_RENAME:
|
904 |
v9fs_string_init(&path); |
905 |
v9fs_string_init(&oldpath); |
906 |
retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, |
907 |
"ss", &oldpath, &path);
|
908 |
if (retval > 0) { |
909 |
retval = rename(oldpath.data, path.data); |
910 |
if (retval < 0) { |
911 |
retval = -errno; |
912 |
} |
913 |
} |
914 |
v9fs_string_free(&oldpath); |
915 |
v9fs_string_free(&path); |
916 |
break;
|
917 |
case T_REMOVE:
|
918 |
v9fs_string_init(&path); |
919 |
retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "s", &path);
|
920 |
if (retval > 0) { |
921 |
retval = remove(path.data); |
922 |
if (retval < 0) { |
923 |
retval = -errno; |
924 |
} |
925 |
} |
926 |
v9fs_string_free(&path); |
927 |
break;
|
928 |
case T_LGETXATTR:
|
929 |
case T_LLISTXATTR:
|
930 |
retval = do_getxattr(header.type, &in_iovec, &out_iovec); |
931 |
break;
|
932 |
case T_LSETXATTR:
|
933 |
v9fs_string_init(&path); |
934 |
v9fs_string_init(&name); |
935 |
v9fs_string_init(&value); |
936 |
retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sssdd", &path,
|
937 |
&name, &value, &size, &flags); |
938 |
if (retval > 0) { |
939 |
retval = lsetxattr(path.data, |
940 |
name.data, value.data, size, flags); |
941 |
if (retval < 0) { |
942 |
retval = -errno; |
943 |
} |
944 |
} |
945 |
v9fs_string_free(&path); |
946 |
v9fs_string_free(&name); |
947 |
v9fs_string_free(&value); |
948 |
break;
|
949 |
case T_LREMOVEXATTR:
|
950 |
v9fs_string_init(&path); |
951 |
v9fs_string_init(&name); |
952 |
retval = proxy_unmarshal(&in_iovec, |
953 |
PROXY_HDR_SZ, "ss", &path, &name);
|
954 |
if (retval > 0) { |
955 |
retval = lremovexattr(path.data, name.data); |
956 |
if (retval < 0) { |
957 |
retval = -errno; |
958 |
} |
959 |
} |
960 |
v9fs_string_free(&path); |
961 |
v9fs_string_free(&name); |
962 |
break;
|
963 |
case T_GETVERSION:
|
964 |
retval = do_getversion(&in_iovec, &out_iovec); |
965 |
break;
|
966 |
default:
|
967 |
goto err_out;
|
968 |
break;
|
969 |
} |
970 |
|
971 |
if (process_reply(sock, header.type, &out_iovec, retval) < 0) { |
972 |
goto err_out;
|
973 |
} |
974 |
} |
975 |
err_out:
|
976 |
g_free(in_iovec.iov_base); |
977 |
g_free(out_iovec.iov_base); |
978 |
return -1; |
979 |
} |
980 |
|
981 |
int main(int argc, char **argv) |
982 |
{ |
983 |
int sock;
|
984 |
uid_t own_u; |
985 |
gid_t own_g; |
986 |
char *rpath = NULL; |
987 |
char *sock_name = NULL; |
988 |
struct stat stbuf;
|
989 |
int c, option_index;
|
990 |
#ifdef FS_IOC_GETVERSION
|
991 |
int retval;
|
992 |
struct statfs st_fs;
|
993 |
#endif
|
994 |
|
995 |
is_daemon = true;
|
996 |
sock = -1;
|
997 |
own_u = own_g = -1;
|
998 |
while (1) { |
999 |
option_index = 0;
|
1000 |
c = getopt_long(argc, argv, "p:nh?f:s:u:g:", helper_opts,
|
1001 |
&option_index); |
1002 |
if (c == -1) { |
1003 |
break;
|
1004 |
} |
1005 |
switch (c) {
|
1006 |
case 'p': |
1007 |
rpath = strdup(optarg); |
1008 |
break;
|
1009 |
case 'n': |
1010 |
is_daemon = false;
|
1011 |
break;
|
1012 |
case 'f': |
1013 |
sock = atoi(optarg); |
1014 |
break;
|
1015 |
case 's': |
1016 |
sock_name = strdup(optarg); |
1017 |
break;
|
1018 |
case 'u': |
1019 |
own_u = atoi(optarg); |
1020 |
break;
|
1021 |
case 'g': |
1022 |
own_g = atoi(optarg); |
1023 |
break;
|
1024 |
case '?': |
1025 |
case 'h': |
1026 |
default:
|
1027 |
usage(argv[0]);
|
1028 |
exit(EXIT_FAILURE); |
1029 |
} |
1030 |
} |
1031 |
|
1032 |
/* Parameter validation */
|
1033 |
if ((sock_name == NULL && sock == -1) || rpath == NULL) { |
1034 |
fprintf(stderr, "socket, socket descriptor or path not specified\n");
|
1035 |
usage(argv[0]);
|
1036 |
return -1; |
1037 |
} |
1038 |
|
1039 |
if (sock_name && sock != -1) { |
1040 |
fprintf(stderr, "both named socket and socket descriptor specified\n");
|
1041 |
usage(argv[0]);
|
1042 |
exit(EXIT_FAILURE); |
1043 |
} |
1044 |
|
1045 |
if (sock_name && (own_u == -1 || own_g == -1)) { |
1046 |
fprintf(stderr, "owner uid:gid not specified, ");
|
1047 |
fprintf(stderr, |
1048 |
"owner uid:gid specifies who can access the socket file\n");
|
1049 |
usage(argv[0]);
|
1050 |
exit(EXIT_FAILURE); |
1051 |
} |
1052 |
|
1053 |
if (lstat(rpath, &stbuf) < 0) { |
1054 |
fprintf(stderr, "invalid path \"%s\" specified, %s\n",
|
1055 |
rpath, strerror(errno)); |
1056 |
exit(EXIT_FAILURE); |
1057 |
} |
1058 |
|
1059 |
if (!S_ISDIR(stbuf.st_mode)) {
|
1060 |
fprintf(stderr, "specified path \"%s\" is not directory\n", rpath);
|
1061 |
exit(EXIT_FAILURE); |
1062 |
} |
1063 |
|
1064 |
if (is_daemon) {
|
1065 |
if (daemon(0, 0) < 0) { |
1066 |
fprintf(stderr, "daemon call failed\n");
|
1067 |
exit(EXIT_FAILURE); |
1068 |
} |
1069 |
openlog(PROGNAME, LOG_PID, LOG_DAEMON); |
1070 |
} |
1071 |
|
1072 |
do_log(LOG_INFO, "Started\n");
|
1073 |
if (sock_name) {
|
1074 |
sock = proxy_socket(sock_name, own_u, own_g); |
1075 |
if (sock < 0) { |
1076 |
goto error;
|
1077 |
} |
1078 |
} |
1079 |
|
1080 |
get_version = false;
|
1081 |
#ifdef FS_IOC_GETVERSION
|
1082 |
/* check whether underlying FS support IOC_GETVERSION */
|
1083 |
retval = statfs(rpath, &st_fs); |
1084 |
if (!retval) {
|
1085 |
switch (st_fs.f_type) {
|
1086 |
case EXT2_SUPER_MAGIC:
|
1087 |
case BTRFS_SUPER_MAGIC:
|
1088 |
case REISERFS_SUPER_MAGIC:
|
1089 |
case XFS_SUPER_MAGIC:
|
1090 |
get_version = true;
|
1091 |
break;
|
1092 |
} |
1093 |
} |
1094 |
#endif
|
1095 |
|
1096 |
if (chdir("/") < 0) { |
1097 |
do_perror("chdir");
|
1098 |
goto error;
|
1099 |
} |
1100 |
if (chroot(rpath) < 0) { |
1101 |
do_perror("chroot");
|
1102 |
goto error;
|
1103 |
} |
1104 |
umask(0);
|
1105 |
|
1106 |
if (init_capabilities() < 0) { |
1107 |
goto error;
|
1108 |
} |
1109 |
|
1110 |
process_requests(sock); |
1111 |
error:
|
1112 |
do_log(LOG_INFO, "Done\n");
|
1113 |
closelog(); |
1114 |
return 0; |
1115 |
} |