#include #include #include #include #include #include #include #include #include "rmtfs.h" #define MAX_CALLERS 10 #define STORAGE_MAX_SIZE (16 * 1024 * 1024) #define BY_PARTLABEL_PATH "/dev/disk/by-partlabel" #define MIN(x, y) ((x) < (y) ? (x) : (y)) struct partition { const char *path; const char *actual; const char *partlabel; }; struct rmtfd { unsigned id; unsigned node; int fd; unsigned dev_error; const struct partition *partition; void *shadow_buf; size_t shadow_len; }; static const char *storage_dir = "/boot"; static int storage_read_only; static int storage_use_partitions; static const struct partition partition_table[] = { { "/boot/modem_fs1", "modem_fs1", "modemst1" }, { "/boot/modem_fs2", "modem_fs2", "modemst2" }, { "/boot/modem_fsc", "modem_fsc", "fsc" }, { "/boot/modem_fsg", "modem_fsg", "fsg" }, { "/boot/modem_tunning", "modem_tunning", "tunning" }, {} }; static struct rmtfd rmtfds[MAX_CALLERS]; static int storage_populate_shadow_buf(struct rmtfd *rmtfd, const char *file); int storage_init(const char *storage_root, bool read_only, bool use_partitions) { int i; if (storage_root) storage_dir = storage_root; if (use_partitions) { if (!storage_root) storage_dir = BY_PARTLABEL_PATH; storage_use_partitions = true; } storage_read_only = read_only; for (i = 0; i < MAX_CALLERS; i++) { rmtfds[i].id = i; rmtfds[i].fd = -1; rmtfds[i].shadow_buf = NULL; } return 0; } struct rmtfd *storage_open(unsigned node, const char *path) { char *fspath; const struct partition *part; struct rmtfd *rmtfd = NULL; const char *file; size_t pathlen; int saved_errno; int ret; int fd; int i; for (part = partition_table; part->path; part++) { if (strcmp(part->path, path) == 0) goto found; } fprintf(stderr, "[RMTFS storage] request for unknown partition '%s', rejecting\n", path); return NULL; found: /* Check if this node already has the requested path open */ for (i = 0; i < MAX_CALLERS; i++) { if ((rmtfds[i].fd != -1 || rmtfds[i].shadow_buf) && rmtfds[i].node == node && rmtfds[i].partition == part) return &rmtfds[i]; } for (i = 0; i < MAX_CALLERS; i++) { if (rmtfds[i].fd == -1 && !rmtfds[i].shadow_buf) { rmtfd = &rmtfds[i]; break; } } if (!rmtfd) { fprintf(stderr, "[storage] out of free rmtfd handles\n"); return NULL; } if (storage_use_partitions) file = part->partlabel; else file = part->actual; pathlen = strlen(storage_dir) + strlen(file) + 2; fspath = alloca(pathlen); snprintf(fspath, pathlen, "%s/%s", storage_dir, file); if (!storage_read_only) { fd = open(fspath, O_RDWR | O_SYNC); if (fd < 0) { saved_errno = errno; fprintf(stderr, "[storage] failed to open '%s' (requested '%s'): %s\n", fspath, part->path, strerror(saved_errno)); errno = saved_errno; return NULL; } rmtfd->fd = fd; rmtfd->shadow_len = 0; } else { ret = storage_populate_shadow_buf(rmtfd, fspath); if (ret < 0) { saved_errno = errno; fprintf(stderr, "[storage] failed to open '%s' (requested '%s'): %s\n", fspath, part->path, strerror(saved_errno)); errno = saved_errno; return NULL; } } rmtfd->node = node; rmtfd->partition = part; return rmtfd; } void storage_close(struct rmtfd *rmtfd) { if (rmtfd->fd >= 0) { close(rmtfd->fd); rmtfd->fd = -1; } free(rmtfd->shadow_buf); rmtfd->shadow_buf = NULL; rmtfd->shadow_len = 0; rmtfd->partition = NULL; } struct rmtfd *storage_get(unsigned node, int caller_id) { struct rmtfd *rmtfd; if (caller_id >= MAX_CALLERS) return NULL; rmtfd = &rmtfds[caller_id]; if (rmtfd->node != node) return NULL; return rmtfd; } int storage_get_caller_id(const struct rmtfd *rmtfd) { return rmtfd->id; } int storage_get_error(const struct rmtfd *rmtfd) { return rmtfd->dev_error; } void storage_exit(void) { int i; for (i = 0; i < MAX_CALLERS; i++) storage_close(&rmtfds[i]); } ssize_t storage_pread(const struct rmtfd *rmtfd, void *buf, size_t nbyte, off_t offset) { ssize_t n; if (!storage_read_only) { n = pread(rmtfd->fd, buf, nbyte, offset); } else { n = MIN(nbyte, rmtfd->shadow_len - offset); if (n > 0) memcpy(buf, (char*)rmtfd->shadow_buf + offset, n); else n = 0; } if (n < nbyte) memset((char*)buf + n, 0, nbyte - n); return nbyte; } ssize_t storage_pwrite(struct rmtfd *rmtfd, const void *buf, size_t nbyte, off_t offset) { size_t new_len = offset + nbyte; void *new_buf; if (!storage_read_only) return pwrite(rmtfd->fd, buf, nbyte, offset); if (new_len >= STORAGE_MAX_SIZE) { fprintf(stderr, "write to %zd bytes exceededs max size\n", new_len); errno = -EINVAL; return -1; } if (new_len > rmtfd->shadow_len) { new_buf = realloc(rmtfd->shadow_buf, new_len); if (!new_buf) { errno = -ENOMEM; return -1; } rmtfd->shadow_buf = new_buf; rmtfd->shadow_len = new_len; } memcpy((char*)rmtfd->shadow_buf + offset, buf, nbyte); return nbyte; } static int storage_populate_shadow_buf(struct rmtfd *rmtfd, const char *file) { ssize_t len; ssize_t n; void *buf; int ret; int fd; fd = open(file, O_RDONLY); if (fd < 0) return -1; len = lseek(fd, 0, SEEK_END); if (len < 0) { ret = -1; goto err_close_fd; } lseek(fd, 0, SEEK_SET); buf = calloc(1, len); if (!buf) { ret = -1; goto err_close_fd; } n = read(fd, buf, len); if (n < 0) { ret = -1; goto err_close_fd; } rmtfd->shadow_buf = buf; rmtfd->shadow_len = n; ret = 0; err_close_fd: close(fd); return ret; }