Statistics
| Branch: | Revision:

root / fsdev / virtfs-proxy-helper.c @ 263ddcc8

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
}