Annotation of qemu/fsdev/virtfs-proxy-helper.c, revision 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.