Statistics
| Branch: | Revision:

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
}