#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "qmi_rmtfs.h" #include "util.h" #include "rmtfs.h" #define RMTFS_QMI_SERVICE 14 #define RMTFS_QMI_VERSION 1 #define RMTFS_QMI_INSTANCE 0 static struct rmtfs_mem *rmem; static sig_atomic_t sig_int_count; static bool dbgprintf_enabled; static void dbgprintf(const char *fmt, ...) { va_list ap; if (!dbgprintf_enabled) return; va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); } static void qmi_result_error(struct rmtfs_qmi_result *result, unsigned error) { /* Only propagate initial error */ if (result->result == QMI_RMTFS_RESULT_FAILURE) return; result->result = QMI_RMTFS_RESULT_FAILURE; result->error = error; } static void rmtfs_open(int sock, const struct qrtr_packet *pkt) { struct rmtfs_open_resp resp = {}; struct rmtfs_open_req req = {}; DEFINE_QRTR_PACKET(resp_buf, 256); struct rmtfd *rmtfd; unsigned int txn; ssize_t len; int caller_id = -1; int ret; ret = qmi_decode_message(&req, &txn, pkt, QMI_REQUEST, QMI_RMTFS_OPEN, rmtfs_open_req_ei); if (ret < 0) { qmi_result_error(&resp.result, QMI_RMTFS_ERR_MALFORMED_MSG); goto respond; } rmtfd = storage_open(pkt->node, req.path); if (!rmtfd) { qmi_result_error(&resp.result, QMI_RMTFS_ERR_INTERNAL); goto respond; } caller_id = storage_get_caller_id(rmtfd); resp.caller_id = caller_id; resp.caller_id_valid = true; respond: dbgprintf("[RMTFS] open %s => %d (%d:%d)\n", req.path, caller_id, resp.result.result, resp.result.error); len = qmi_encode_message(&resp_buf, QMI_RESPONSE, QMI_RMTFS_OPEN, txn, &resp, rmtfs_open_resp_ei); if (len < 0) { fprintf(stderr, "[RMTFS] failed to encode open-response: %s\n", strerror(-len)); return; } ret = qrtr_sendto(sock, pkt->node, pkt->port, resp_buf.data, resp_buf.data_len); if (ret < 0) fprintf(stderr, "[RMTFS] failed to send open-response: %s\n", strerror(-ret)); } static void rmtfs_close(int sock, const struct qrtr_packet *pkt) { struct rmtfs_close_resp resp = {}; struct rmtfs_close_req req = {}; DEFINE_QRTR_PACKET(resp_buf, 256); struct rmtfd *rmtfd; unsigned int txn; ssize_t len; int ret; ret = qmi_decode_message(&req, &txn, pkt, QMI_REQUEST, QMI_RMTFS_CLOSE, rmtfs_close_req_ei); if (ret < 0) { qmi_result_error(&resp.result, QMI_RMTFS_ERR_MALFORMED_MSG); goto respond; } rmtfd = storage_get(pkt->node, req.caller_id); if (!rmtfd) { qmi_result_error(&resp.result, QMI_RMTFS_ERR_INTERNAL); goto respond; } storage_close(rmtfd); rmtfs_mem_free(rmem); respond: dbgprintf("[RMTFS] close %s => %d (%d:%d)\n", req.caller_id, resp.result.result, resp.result.error); len = qmi_encode_message(&resp_buf, QMI_RESPONSE, QMI_RMTFS_CLOSE, txn, &resp, rmtfs_close_resp_ei); if (len < 0) { fprintf(stderr, "[RMTFS] failed to encode close-response: %s\n", strerror(-len)); return; } ret = qrtr_sendto(sock, pkt->node, pkt->port, resp_buf.data, resp_buf.data_len); if (ret < 0) fprintf(stderr, "[RMTFS] failed to send close-response: %s\n", strerror(-ret)); } static void rmtfs_iovec(int sock, struct qrtr_packet *pkt) { struct rmtfs_iovec_entry *entries; struct rmtfs_iovec_resp resp = {}; struct rmtfs_iovec_req req = {}; DEFINE_QRTR_PACKET(resp_buf, 256); struct rmtfd *rmtfd; uint32_t caller_id = 0; size_t num_entries = 0; off_t sector_base; uint8_t is_write; off_t phys_base; uint8_t force = 0; unsigned txn; off_t offset; ssize_t len; ssize_t n; char buf[SECTOR_SIZE]; int ret; int i; int j; ret = qmi_decode_message(&req, &txn, pkt, QMI_REQUEST, QMI_RMTFS_RW_IOVEC, rmtfs_iovec_req_ei); if (ret < 0) { qmi_result_error(&resp.result, QMI_RMTFS_ERR_MALFORMED_MSG); goto respond; } caller_id = req.caller_id; is_write = req.direction; entries = req.iovec; num_entries = req.iovec_len; force = req.is_force_sync; rmtfd = storage_get(pkt->node, caller_id); if (!rmtfd) { fprintf(stderr, "[RMTFS] iovec request for non-existing caller\n"); qmi_result_error(&resp.result, QMI_RMTFS_ERR_INTERNAL); goto respond; } for (i = 0; i < num_entries; i++) { phys_base = entries[i].phys_offset; sector_base = entries[i].sector_addr * SECTOR_SIZE; offset = 0; for (j = 0; j < entries[i].num_sector; j++) { if (is_write) { n = rmtfs_mem_read(rmem, phys_base + offset, buf, SECTOR_SIZE); if (n == SECTOR_SIZE) n = storage_pwrite(rmtfd, buf, n, sector_base + offset); } else { n = storage_pread(rmtfd, buf, SECTOR_SIZE, sector_base + offset); if (n >= 0) { if (n < SECTOR_SIZE) memset(buf + n, 0, SECTOR_SIZE - n); n = rmtfs_mem_write(rmem, phys_base + offset, buf, SECTOR_SIZE); } } if (n != SECTOR_SIZE) { fprintf(stderr, "[RMTFS] failed to %s sector %d\n", is_write ? "write" : "read", entries[i].sector_addr + j); qmi_result_error(&resp.result, QMI_RMTFS_ERR_INTERNAL); goto respond; } offset += SECTOR_SIZE; } } respond: dbgprintf("[RMTFS] iovec %d, %sforced => (%d:%d)\n", caller_id, force ? "" : "not ", resp.result.result, resp.result.error); for (i = 0; i < num_entries; i++) { dbgprintf("[RMTFS] %s %d:%d 0x%x\n", is_write ? "write" : "read", entries[i].sector_addr, entries[i].num_sector, entries[i].phys_offset); } len = qmi_encode_message(&resp_buf, QMI_RESPONSE, QMI_RMTFS_RW_IOVEC, txn, &resp, rmtfs_iovec_resp_ei); if (len < 0) { fprintf(stderr, "[RMTFS] failed to encode iovec-response: %s\n", strerror(-len)); return; } ret = qrtr_sendto(sock, pkt->node, pkt->port, resp_buf.data, resp_buf.data_len); if (ret < 0) fprintf(stderr, "[RMTFS] failed to send iovec-response: %s\n", strerror(-ret)); } static void rmtfs_alloc_buf(int sock, struct qrtr_packet *pkt) { struct rmtfs_alloc_buf_resp resp = {}; struct rmtfs_alloc_buf_req req = {}; DEFINE_QRTR_PACKET(resp_buf, 256); uint32_t alloc_size = 0; uint32_t caller_id = 0; int64_t address = 0; unsigned txn; ssize_t len; int ret; ret = qmi_decode_message(&req, &txn, pkt, QMI_REQUEST, QMI_RMTFS_ALLOC_BUFF, rmtfs_alloc_buf_req_ei); if (ret < 0) { qmi_result_error(&resp.result, QMI_RMTFS_ERR_MALFORMED_MSG); goto respond; } caller_id = req.caller_id; alloc_size = req.buff_size; address = rmtfs_mem_alloc(rmem, alloc_size); if (address < 0) { qmi_result_error(&resp.result, QMI_RMTFS_ERR_INTERNAL); goto respond; } resp.buff_address = address; resp.buff_address_valid = true; respond: dbgprintf("[RMTFS] alloc %d, %d => 0x%lx (%d:%d)\n", caller_id, alloc_size, address, resp.result.result, resp.result.error); len = qmi_encode_message(&resp_buf, QMI_RESPONSE, QMI_RMTFS_ALLOC_BUFF, txn, &resp, rmtfs_alloc_buf_resp_ei); if (len < 0) { fprintf(stderr, "[RMTFS] failed to encode alloc-buf-response: %s\n", strerror(-len)); return; } ret = qrtr_sendto(sock, pkt->node, pkt->port, resp_buf.data, resp_buf.data_len); if (ret < 0) fprintf(stderr, "[RMTFS] failed to send alloc-buf-response: %s\n", strerror(-ret)); } static void rmtfs_get_dev_error(int sock, struct qrtr_packet *pkt) { struct rmtfs_dev_error_resp resp = {}; struct rmtfs_dev_error_req req = {}; DEFINE_QRTR_PACKET(resp_buf, 256); struct rmtfd *rmtfd; unsigned txn; ssize_t len; int ret; ret = qmi_decode_message(&req, &txn, pkt, QMI_REQUEST, QMI_RMTFS_GET_DEV_ERROR, rmtfs_dev_error_req_ei); if (ret < 0) { qmi_result_error(&resp.result, QMI_RMTFS_ERR_MALFORMED_MSG); goto respond; } rmtfd = storage_get(pkt->node, req.caller_id); if (rmtfd) { qmi_result_error(&resp.result, QMI_RMTFS_ERR_INTERNAL); goto respond; } resp.status = storage_get_error(rmtfd); resp.status_valid = true; respond: dbgprintf("[RMTFS] dev_error %d => %d (%d:%d)\n", req.caller_id, resp.status, resp.result.result, resp.result.error); len = qmi_encode_message(&resp_buf, QMI_RESPONSE, QMI_RMTFS_GET_DEV_ERROR, txn, &resp, rmtfs_dev_error_resp_ei); if (len < 0) { fprintf(stderr, "[RMTFS] failed to encode dev-error-response: %s\n", strerror(-len)); return; } ret = qrtr_sendto(sock, pkt->node, pkt->port, resp_buf.data, resp_buf.data_len); if (ret < 0) fprintf(stderr, "[RMTFS] failed to send dev-error-response: %s\n", strerror(-ret)); } static int rmtfs_bye(uint32_t node) { dbgprintf("[RMTFS] bye from %d\n", node); return 0; } static int rmtfs_del_client(uint32_t node, uint32_t port) { dbgprintf("[RMTFS] del_client %d:%d\n", node, port); return 0; } static int handle_rmtfs(int sock) { struct sockaddr_qrtr sq; struct qrtr_packet pkt; unsigned int msg_id; socklen_t sl; char buf[4096]; int ret; sl = sizeof(sq); ret = recvfrom(sock, buf, sizeof(buf), 0, (void *)&sq, &sl); if (ret < 0) { ret = -errno; if (ret != -ENETRESET) fprintf(stderr, "[RMTFS] recvfrom failed: %d\n", ret); return ret; } dbgprintf("[RMTFS] packet; from: %d:%d\n", sq.sq_node, sq.sq_port); ret = qrtr_decode(&pkt, buf, ret, &sq); if (ret < 0) { fprintf(stderr, "[RMTFS] unable to decode qrtr packet\n"); return ret; } switch (pkt.type) { case QRTR_TYPE_BYE: return rmtfs_bye(pkt.node); case QRTR_TYPE_DEL_CLIENT: return rmtfs_del_client(pkt.node, pkt.port); case QRTR_TYPE_DATA: ret = qmi_decode_header(&pkt, &msg_id); if (ret < 0) return ret; switch (msg_id) { case QMI_RMTFS_OPEN: rmtfs_open(sock, &pkt); break; case QMI_RMTFS_CLOSE: rmtfs_close(sock, &pkt); break; case QMI_RMTFS_RW_IOVEC: rmtfs_iovec(sock, &pkt); break; case QMI_RMTFS_ALLOC_BUFF: rmtfs_alloc_buf(sock, &pkt); break; case QMI_RMTFS_GET_DEV_ERROR: rmtfs_get_dev_error(sock, &pkt); break; default: fprintf(stderr, "[RMTFS] Unknown request: %d\n", msg_id); break; } return 0; } return ret; } static int sig_int_count; static int run_rmtfs(int rprocfd) { bool sig_int_handled = false; int rmtfs_fd; fd_set rfds; char done; int nfds; int ret; rmtfs_fd = qrtr_open(RMTFS_QMI_SERVICE); if (rmtfs_fd < 0) { fprintf(stderr, "failed to create qrtr socket\n"); return rmtfs_fd; } dbgprintf("registering services\n"); ret = qrtr_publish(rmtfs_fd, RMTFS_QMI_SERVICE, RMTFS_QMI_VERSION, RMTFS_QMI_INSTANCE); if (ret < 0) { fprintf(stderr, "failed to publish rmtfs service"); return ret; } if (rprocfd >= 0) rproc_start(); for (;;) { if (rprocfd >= 0 && sig_int_count == 1 && !sig_int_handled) { rproc_stop(); sig_int_handled = true; } else if (sig_int_count > 1) { break; } FD_ZERO(&rfds); FD_SET(rmtfs_fd, &rfds); if (rprocfd >= 0) FD_SET(rprocfd, &rfds); nfds = MAX(rmtfs_fd, rprocfd) + 1; ret = select(nfds, &rfds, NULL, NULL, NULL); if (ret < 0 && errno != EINTR) break; else if (ret < 0 && errno == EINTR) continue; if (rprocfd >= 0 && FD_ISSET(rprocfd, &rfds)) { ret = read(rprocfd, &done, 1); if (!ret || done == 'Y') break; } if (FD_ISSET(rmtfs_fd, &rfds)) { ret = handle_rmtfs(rmtfs_fd); if (ret == -ENETRESET) break; } } close(rmtfs_fd); return ret; } static void sig_int_handler(int signo __unused) { sig_int_count++; } int main(int argc, char **argv) { struct sigaction action; bool use_partitions = false; bool read_only = false; int rprocfd = -1; int ret; int option; const char *storage_root = NULL; while ((option = getopt(argc, argv, "o:Prsv")) != -1) { switch (option) { /* * -o sets the directory where EFS images are stored, * or sets the directory from where raw EFS partitions * can be picked by-name when used with -P option. */ case 'o': storage_root = optarg; break; /* -P to find and use raw EFS partitions */ case 'P': use_partitions = true; break; /* -r to avoid writing to storage */ case 'r': read_only = true; break; /* enable sync for the mss rproc instance */ case 's': rprocfd = rproc_init(); if (rprocfd < 0) { fprintf(stderr, "Failed to get rprocfd\n"); return 1; } break; /* -v is for verbose */ case 'v': dbgprintf_enabled = 1; break; case '?': fprintf(stderr, "Unknown option: -%c\n", option); return 1; } } sigemptyset(&action.sa_mask); action.sa_handler = sig_int_handler; action.sa_flags = 0; sigaction(SIGINT, &action, NULL); sigaction(SIGTERM, &action, NULL); rmem = rmtfs_mem_open(); if (!rmem) return 1; ret = storage_init(storage_root, read_only, use_partitions); if (ret) { fprintf(stderr, "failed to initialize storage system\n"); goto close_rmtfs_mem; } do { ret = run_rmtfs(rprocfd); } while (ret == -ENETRESET); storage_exit(); close_rmtfs_mem: rmtfs_mem_close(rmem); return 0; }