Annotation of qemu/fsdev/virtfs-proxy-helper.c, revision 1.1.1.1

1.1       root        1: /*
                      2:  * Helper for QEMU Proxy FS Driver
                      3:  * Copyright IBM, Corp. 2011
                      4:  *
                      5:  * Authors:
                      6:  * M. Mohan Kumar <[email protected]>
                      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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.