/* * return < 0 on transport error. * *status is valid only if return >= 0 */ static int v9fs_receive_status(V9fsProxy *proxy, struct iovec *reply, int *status) { int retval; ProxyHeader header; *status = 0; reply->iov_len = 0; retval = socket_read(proxy->sockfd, reply->iov_base, PROXY_HDR_SZ); if (retval < 0) { return retval; } reply->iov_len = PROXY_HDR_SZ; proxy_unmarshal(reply, 0, "dd", &header.type, &header.size); if (header.size != sizeof(int)) { *status = -ENOBUFS; return 0; } retval = socket_read(proxy->sockfd, reply->iov_base + PROXY_HDR_SZ, header.size); if (retval < 0) { return retval; } reply->iov_len += header.size; proxy_unmarshal(reply, PROXY_HDR_SZ, "d", status); return 0; }
/* * create other filesystem objects and send 0 on success * return -errno on error */ static int do_create_others(int type, struct iovec *iovec) { dev_t rdev; int retval = 0; int offset = PROXY_HDR_SZ; V9fsString oldpath, path; int mode, uid, gid, cur_uid, cur_gid; v9fs_string_init(&path); v9fs_string_init(&oldpath); cur_uid = geteuid(); cur_gid = getegid(); retval = proxy_unmarshal(iovec, offset, "dd", &uid, &gid); if (retval < 0) { return retval; } offset += retval; retval = setfsugid(uid, gid); if (retval < 0) { retval = -errno; goto err_out; } switch (type) { case T_MKNOD: retval = proxy_unmarshal(iovec, offset, "sdq", &path, &mode, &rdev); if (retval < 0) { goto err_out; } retval = mknod(path.data, mode, rdev); break; case T_MKDIR: retval = proxy_unmarshal(iovec, offset, "sd", &path, &mode); if (retval < 0) { goto err_out; } retval = mkdir(path.data, mode); break; case T_SYMLINK: retval = proxy_unmarshal(iovec, offset, "ss", &oldpath, &path); if (retval < 0) { goto err_out; } retval = symlink(oldpath.data, path.data); break; } if (retval < 0) { retval = -errno; } err_out: v9fs_string_free(&path); v9fs_string_free(&oldpath); setfsugid(cur_uid, cur_gid); return retval; }
static int do_getxattr(int type, struct iovec *iovec, struct iovec *out_iovec) { int size = 0, offset, retval; V9fsString path, name, xattr; v9fs_string_init(&xattr); v9fs_string_init(&path); retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "ds", &size, &path); if (retval < 0) { return retval; } offset = PROXY_HDR_SZ + retval; if (size) { xattr.data = g_malloc(size); xattr.size = size; } switch (type) { case T_LGETXATTR: v9fs_string_init(&name); retval = proxy_unmarshal(iovec, offset, "s", &name); if (retval > 0) { retval = lgetxattr(path.data, name.data, xattr.data, size); if (retval < 0) { retval = -errno; } else { xattr.size = retval; } } v9fs_string_free(&name); break; case T_LLISTXATTR: retval = llistxattr(path.data, xattr.data, size); if (retval < 0) { retval = -errno; } else { xattr.size = retval; } break; } if (retval < 0) { goto err_out; } if (!size) { proxy_marshal(out_iovec, PROXY_HDR_SZ, "d", retval); retval = sizeof(retval); } else { retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "s", &xattr); } err_out: v9fs_string_free(&xattr); v9fs_string_free(&path); return retval; }
/* * create a file and send fd on success * return -errno on error */ static int do_create(struct iovec *iovec) { int ret; V9fsString path; int flags, mode, uid, gid, cur_uid, cur_gid; v9fs_string_init(&path); ret = proxy_unmarshal(iovec, PROXY_HDR_SZ, "sdddd", &path, &flags, &mode, &uid, &gid); if (ret < 0) { goto unmarshal_err_out; } ret = setugid(uid, gid, &cur_uid, &cur_gid); if (ret < 0) { goto unmarshal_err_out; } ret = open(path.data, flags, mode); if (ret < 0) { ret = -errno; } resetugid(cur_uid, cur_gid); unmarshal_err_out: v9fs_string_free(&path); return ret; }
static int do_readlink(struct iovec *iovec, struct iovec *out_iovec) { char *buffer; int size, retval; V9fsString target, path; v9fs_string_init(&path); retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "sd", &path, &size); if (retval < 0) { v9fs_string_free(&path); return retval; } buffer = g_malloc(size); v9fs_string_init(&target); retval = readlink(path.data, buffer, size - 1); if (retval > 0) { buffer[retval] = '\0'; v9fs_string_sprintf(&target, "%s", buffer); retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "s", &target); } else { retval = -errno; } g_free(buffer); v9fs_string_free(&target); v9fs_string_free(&path); return retval; }
static int read_request(int sockfd, struct iovec *iovec, ProxyHeader *header) { int retval; /* * read the request header. */ iovec->iov_len = 0; retval = socket_read(sockfd, iovec->iov_base, PROXY_HDR_SZ); if (retval < 0) { return retval; } iovec->iov_len = PROXY_HDR_SZ; retval = proxy_unmarshal(iovec, 0, "dd", &header->type, &header->size); if (retval < 0) { return retval; } /* * We can't process message.size > PROXY_MAX_IO_SZ. * Treat it as fatal error */ if (header->size > PROXY_MAX_IO_SZ) { return -ENOBUFS; } retval = socket_read(sockfd, iovec->iov_base + PROXY_HDR_SZ, header->size); if (retval < 0) { return retval; } iovec->iov_len += header->size; return 0; }
/* * create a file and send fd on success * return -errno on error */ static int do_create(struct iovec *iovec) { int ret; V9fsString path; int flags, mode, uid, gid, cur_uid, cur_gid; v9fs_string_init(&path); ret = proxy_unmarshal(iovec, PROXY_HDR_SZ, "sdddd", &path, &flags, &mode, &uid, &gid); if (ret < 0) { goto unmarshal_err_out; } cur_uid = geteuid(); cur_gid = getegid(); ret = setfsugid(uid, gid); if (ret < 0) { /* * On failure reset back to the * old uid/gid */ ret = -errno; goto err_out; } ret = open(path.data, flags, mode); if (ret < 0) { LOGV("%s: Could not open file: %s", __func__, path.data); ret = -errno; } err_out: setfsugid(cur_uid, cur_gid); unmarshal_err_out: v9fs_string_free(&path); return ret; }
/* * Gets stat/statfs information and packs in out_iovec structure * on success returns number of bytes packed in out_iovec struture * otherwise returns -errno */ static int do_stat(int type, struct iovec *iovec, struct iovec *out_iovec) { int retval; V9fsString path; ProxyStat pr_stat; ProxyStatFS pr_stfs; struct stat st_buf; struct statfs stfs_buf; v9fs_string_init(&path); retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "s", &path); if (retval < 0) { return retval; } switch (type) { case T_LSTAT: retval = lstat(path.data, &st_buf); if (retval < 0) { retval = -errno; } else { stat_to_prstat(&pr_stat, &st_buf); retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "qqqdddqqqqqqqqqq", pr_stat.st_dev, pr_stat.st_ino, pr_stat.st_nlink, pr_stat.st_mode, pr_stat.st_uid, pr_stat.st_gid, pr_stat.st_rdev, pr_stat.st_size, pr_stat.st_blksize, pr_stat.st_blocks, pr_stat.st_atim_sec, pr_stat.st_atim_nsec, pr_stat.st_mtim_sec, pr_stat.st_mtim_nsec, pr_stat.st_ctim_sec, pr_stat.st_ctim_nsec); } break; case T_STATFS: retval = statfs(path.data, &stfs_buf); if (retval < 0) { retval = -errno; } else { statfs_to_prstatfs(&pr_stfs, &stfs_buf); retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "qqqqqqqqqqq", pr_stfs.f_type, pr_stfs.f_bsize, pr_stfs.f_blocks, pr_stfs.f_bfree, pr_stfs.f_bavail, pr_stfs.f_files, pr_stfs.f_ffree, pr_stfs.f_fsid[0], pr_stfs.f_fsid[1], pr_stfs.f_namelen, pr_stfs.f_frsize); } break; } v9fs_string_free(&path); return retval; }
/* * open a file and send fd on success * return -errno on error */ static int do_open(struct iovec *iovec) { int flags, ret; V9fsString path; v9fs_string_init(&path); ret = proxy_unmarshal(iovec, PROXY_HDR_SZ, "sd", &path, &flags); if (ret < 0) { goto err_out; } ret = open(path.data, flags); if (ret < 0) { ret = -errno; } err_out: v9fs_string_free(&path); return ret; }
/* * gets generation number * returns -errno on failure and sizeof(generation number) on success */ static int do_getversion(struct iovec *iovec, struct iovec *out_iovec) { uint64_t version; int retval = -ENOTTY; #ifdef FS_IOC_GETVERSION int fd; V9fsString path; #endif /* no need to issue ioctl */ if (!get_version) { version = 0; retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "q", version); return retval; } #ifdef FS_IOC_GETVERSION retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "s", &path); if (retval < 0) { return retval; } fd = open(path.data, O_RDONLY); if (fd < 0) { retval = -errno; goto err_out; } if (ioctl(fd, FS_IOC_GETVERSION, &version) < 0) { retval = -errno; } else { retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "q", version); } close(fd); err_out: v9fs_string_free(&path); #endif return retval; }
/* * Response contains two parts * {header, data} * header.type == T_ERROR, data -> -errno * header.type == T_SUCCESS, data -> response * size of errno/response is given by header.size * returns < 0, on transport error. response is * valid only if status >= 0. */ static int v9fs_receive_response(V9fsProxy *proxy, int type, int *status, void *response) { int retval; ProxyHeader header; struct iovec *reply = &proxy->in_iovec; *status = 0; reply->iov_len = 0; retval = socket_read(proxy->sockfd, reply->iov_base, PROXY_HDR_SZ); if (retval < 0) { return retval; } reply->iov_len = PROXY_HDR_SZ; proxy_unmarshal(reply, 0, "dd", &header.type, &header.size); /* * if response size > PROXY_MAX_IO_SZ, read the response but ignore it and * return -ENOBUFS */ if (header.size > PROXY_MAX_IO_SZ) { int count; while (header.size > 0) { count = MIN(PROXY_MAX_IO_SZ, header.size); count = socket_read(proxy->sockfd, reply->iov_base, count); if (count < 0) { return count; } header.size -= count; } *status = -ENOBUFS; return 0; } retval = socket_read(proxy->sockfd, reply->iov_base + PROXY_HDR_SZ, header.size); if (retval < 0) { return retval; } reply->iov_len += header.size; /* there was an error during processing request */ if (header.type == T_ERROR) { int ret; ret = proxy_unmarshal(reply, PROXY_HDR_SZ, "d", status); if (ret < 0) { *status = ret; } return 0; } switch (type) { case T_LSTAT: { ProxyStat prstat; retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "qqqdddqqqqqqqqqq", &prstat.st_dev, &prstat.st_ino, &prstat.st_nlink, &prstat.st_mode, &prstat.st_uid, &prstat.st_gid, &prstat.st_rdev, &prstat.st_size, &prstat.st_blksize, &prstat.st_blocks, &prstat.st_atim_sec, &prstat.st_atim_nsec, &prstat.st_mtim_sec, &prstat.st_mtim_nsec, &prstat.st_ctim_sec, &prstat.st_ctim_nsec); prstat_to_stat(response, &prstat); break; } case T_STATFS: { ProxyStatFS prstfs; retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "qqqqqqqqqqq", &prstfs.f_type, &prstfs.f_bsize, &prstfs.f_blocks, &prstfs.f_bfree, &prstfs.f_bavail, &prstfs.f_files, &prstfs.f_ffree, &prstfs.f_fsid[0], &prstfs.f_fsid[1], &prstfs.f_namelen, &prstfs.f_frsize); prstatfs_to_statfs(response, &prstfs); break; } case T_READLINK: { V9fsString target; v9fs_string_init(&target); retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "s", &target); strcpy(response, target.data); v9fs_string_free(&target); break; } case T_LGETXATTR: case T_LLISTXATTR: { V9fsString xattr; v9fs_string_init(&xattr); retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "s", &xattr); memcpy(response, xattr.data, xattr.size); v9fs_string_free(&xattr); break; } case T_GETVERSION: proxy_unmarshal(reply, PROXY_HDR_SZ, "q", response); break; default: return -1; } if (retval < 0) { *status = retval; } return 0; }
static int process_requests(int sock) { int flags; int size = 0; int retval = 0; uint64_t offset; ProxyHeader header; int mode, uid, gid; V9fsString name, value; struct timespec spec[2]; V9fsString oldpath, path; struct iovec in_iovec, out_iovec; in_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ); in_iovec.iov_len = PROXY_MAX_IO_SZ + PROXY_HDR_SZ; out_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ); out_iovec.iov_len = PROXY_MAX_IO_SZ + PROXY_HDR_SZ; while (1) { /* * initialize the header type, so that we send * response to proper request type. */ header.type = 0; retval = read_request(sock, &in_iovec, &header); if (retval < 0) { goto err_out; } switch (header.type) { case T_OPEN: retval = do_open(&in_iovec); break; case T_CREATE: retval = do_create(&in_iovec); break; case T_MKNOD: case T_MKDIR: case T_SYMLINK: retval = do_create_others(header.type, &in_iovec); break; case T_LINK: v9fs_string_init(&path); v9fs_string_init(&oldpath); retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "ss", &oldpath, &path); if (retval > 0) { retval = link(oldpath.data, path.data); if (retval < 0) { retval = -errno; } } v9fs_string_free(&oldpath); v9fs_string_free(&path); break; case T_LSTAT: case T_STATFS: retval = do_stat(header.type, &in_iovec, &out_iovec); break; case T_READLINK: retval = do_readlink(&in_iovec, &out_iovec); break; case T_CHMOD: v9fs_string_init(&path); retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sd", &path, &mode); if (retval > 0) { retval = chmod(path.data, mode); if (retval < 0) { retval = -errno; } } v9fs_string_free(&path); break; case T_CHOWN: v9fs_string_init(&path); retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sdd", &path, &uid, &gid); if (retval > 0) { retval = lchown(path.data, uid, gid); if (retval < 0) { retval = -errno; } } v9fs_string_free(&path); break; case T_TRUNCATE: v9fs_string_init(&path); retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sq", &path, &offset); if (retval > 0) { retval = truncate(path.data, offset); if (retval < 0) { retval = -errno; } } v9fs_string_free(&path); break; case T_UTIME: v9fs_string_init(&path); retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sqqqq", &path, &spec[0].tv_sec, &spec[0].tv_nsec, &spec[1].tv_sec, &spec[1].tv_nsec); if (retval > 0) { retval = utimensat(AT_FDCWD, path.data, spec, AT_SYMLINK_NOFOLLOW); if (retval < 0) { retval = -errno; } } v9fs_string_free(&path); break; case T_RENAME: v9fs_string_init(&path); v9fs_string_init(&oldpath); retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "ss", &oldpath, &path); if (retval > 0) { retval = rename(oldpath.data, path.data); if (retval < 0) { retval = -errno; } } v9fs_string_free(&oldpath); v9fs_string_free(&path); break; case T_REMOVE: v9fs_string_init(&path); retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "s", &path); if (retval > 0) { retval = remove(path.data); if (retval < 0) { retval = -errno; } } v9fs_string_free(&path); break; case T_LGETXATTR: case T_LLISTXATTR: retval = do_getxattr(header.type, &in_iovec, &out_iovec); break; case T_LSETXATTR: v9fs_string_init(&path); v9fs_string_init(&name); v9fs_string_init(&value); retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sssdd", &path, &name, &value, &size, &flags); if (retval > 0) { retval = lsetxattr(path.data, name.data, value.data, size, flags); if (retval < 0) { retval = -errno; } } v9fs_string_free(&path); v9fs_string_free(&name); v9fs_string_free(&value); break; case T_LREMOVEXATTR: v9fs_string_init(&path); v9fs_string_init(&name); retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "ss", &path, &name); if (retval > 0) { retval = lremovexattr(path.data, name.data); if (retval < 0) { retval = -errno; } } v9fs_string_free(&path); v9fs_string_free(&name); break; case T_GETVERSION: retval = do_getversion(&in_iovec, &out_iovec); break; default: goto err_out; break; } if (process_reply(sock, header.type, &out_iovec, retval) < 0) { goto err_out; } } err_out: g_free(in_iovec.iov_base); g_free(out_iovec.iov_base); return -1; }