diff options
Diffstat (limited to 'accel-pppd/backup/backup_file.c')
-rw-r--r-- | accel-pppd/backup/backup_file.c | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/accel-pppd/backup/backup_file.c b/accel-pppd/backup/backup_file.c new file mode 100644 index 0000000..b9d2646 --- /dev/null +++ b/accel-pppd/backup/backup_file.c @@ -0,0 +1,351 @@ +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <dirent.h> +#include <sys/stat.h> +#include <sys/uio.h> +#include <sys/mman.h> + +#include "triton.h" +#include "log.h" +#include "ap_session.h" +#include "backup.h" +#include "crypto.h" +#include "memdebug.h" + +#define VERSION 1 + +struct fs_backup_data +{ + struct list_head fd_list; + int fd; + void *map_addr; + int map_len; + char sessionid[AP_SESSIONID_LEN]; + struct backup_data data; +}; + +static char *conf_path; + +static struct backup_storage file_storage; + +static struct backup_data *fs_create(struct ap_session *ses) +{ + struct fs_backup_data *d = _malloc(sizeof(*d)); + + if (!d) + return NULL; + + memset(d, 0, sizeof(*d)); + d->fd = -1; + INIT_LIST_HEAD(&d->fd_list); + INIT_LIST_HEAD(&d->data.mod_list); + d->data.ses = ses; + d->data.storage = &file_storage; + + return &d->data; +} + +static int fs_commit(struct backup_data *d) +{ + char fname[PATH_MAX]; + int fd; + struct backup_mod *mod; + struct backup_tag *tag; + struct iovec iov[IOV_MAX]; + int i, len, n; + MD5_CTX md5; + unsigned char md5_buf[16]; + uint8_t end[4] = {0, 0, 0, 0}; + uint8_t version = VERSION; + uint8_t *ptr; + + if (!conf_path) + return -1; + + sprintf(fname, "%s/%s", conf_path, d->ses->sessionid); + + fd = open(fname, O_WRONLY | O_CREAT | O_TRUNC, S_IREAD | S_IWRITE); + + if (fd < 0) { + log_error("backup: can not create file '%s': %s\n", fname, strerror(errno)); + return -1; + } + + MD5_Init(&md5); + MD5_Update(&md5, &version, 1); + + iov[0].iov_base = &version; + iov[0].iov_len = 1; + i = 1; + len = 1; + + list_for_each_entry(mod, &d->mod_list, entry) { + MD5_Update(&md5, &mod->id, 1); + iov[i].iov_base = &mod->id; + iov[i].iov_len = 1; + i++; + len++; + + list_for_each_entry(tag, &mod->tag_list, entry) { + ptr = (uint8_t *)(tag + 1); + *ptr = tag->id; ptr++; + *ptr = tag->internal ? 1 : 0; ptr++; + *(uint16_t *)ptr = tag->size; + MD5_Update(&md5, tag + 1, 4 + tag->size); + iov[i].iov_base = tag + 1; + iov[i].iov_len = 4 + tag->size; + i++; + len += 4 + tag->size; + if (i == IOV_MAX - 2) { + n = writev(fd, iov, i); + if (n < len) { + log_error("backup: short write %i/%i\n", n, len); + goto out_err; + } + i = 0; + len = 0; + } + } + + MD5_Update(&md5, end, 4); + iov[i].iov_base = end; + iov[i].iov_len = 4; + i++; + len += 4; + } + + MD5_Final(md5_buf, &md5); + + iov[i].iov_base = md5_buf; + iov[i].iov_len = 16; + len += 16; + + n = writev(fd, iov, i + 1); + if (n < len) { + log_error("backup: short write %i/%i\n", n, len); + goto out_err; + } + + close(fd); + + while (!list_empty(&d->mod_list)) { + mod = list_entry(d->mod_list.next, typeof(*mod), entry); + list_del(&mod->entry); + while (!list_empty(&mod->tag_list)) { + tag = list_entry(mod->tag_list.next, typeof(*tag), entry); + list_del(&tag->entry); + _free(tag); + } + _free(mod); + } + + return 0; + +out_err: + close(fd); + unlink(fname); + return -1; +} + +static void fs_free(struct backup_data *d) +{ + struct fs_backup_data *fsd = container_of(d, typeof(*fsd), data); + char fname[PATH_MAX]; + + if (fsd->map_addr) + munmap(fsd->map_addr, fsd->map_len); + + if (fsd->fd != -1) + close(fsd->fd); + + sprintf(fname, "%s/%s", conf_path, d->ses->sessionid); + unlink(fname); + + _free(fsd); +} + +static struct backup_mod *fs_alloc_mod(struct backup_data *d) +{ + struct backup_mod *m = _malloc(sizeof(struct backup_mod)); + + if (!m) + return NULL; + + memset(m, 0, sizeof(*m)); + INIT_LIST_HEAD(&m->tag_list); + + return m; +} + +static void fs_free_mod(struct backup_mod *mod) +{ + _free(mod); +} + +static struct backup_tag *fs_alloc_tag(struct backup_data *d, int size) +{ + struct backup_tag *t = _malloc(sizeof(struct backup_tag) + 4 + size); + + if (!t) + return NULL; + + memset(t, 0, sizeof(*t)); + + t->data = (uint8_t *)(t + 1) + 4; + + return t; +} + +static void fs_free_tag(struct backup_data *d, struct backup_tag *tag) +{ + _free(tag); +} + +static void fs_add_fd(struct backup_data *d, int fd) +{ + +} + +static void restore_session(const char *fn, int internal) +{ + char fname[PATH_MAX]; + int fd; + struct stat st; + uint8_t *ptr, *endptr; + MD5_CTX md5; + unsigned char md5_buf[16]; + struct backup_data *d; + struct fs_backup_data *fsd; + struct backup_mod *mod; + struct backup_tag *tag; + + sprintf(fname, "%s/%s", conf_path, fn); + + fd = open(fname, O_RDONLY); + if (fd < 0) { + log_emerg("backup_file: open '%s': %s\n", fname, strerror(errno)); + return; + } + + fstat(fd, &st); + + ptr = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (ptr == MAP_FAILED) { + log_emerg("backup_file: mmap '%s': %s\n", fname, strerror(errno)); + close(fd); + return; + } + + if (*ptr != VERSION) + goto out; + + MD5_Init(&md5); + MD5_Update(&md5, ptr, st.st_size - 16); + MD5_Final(md5_buf, &md5); + + if (memcmp(md5_buf, ptr + st.st_size - 16, 16)) + goto out; + + d = fs_create(NULL); + if (!d) + goto out; + + d->internal = internal; + + fsd = container_of(d, typeof(*fsd), data); + fsd->fd = fd; + fsd->map_addr = ptr; + fsd->map_len = st.st_size; + + endptr = ptr + st.st_size - 16; + ptr++; + + while (ptr < endptr) { + mod = fs_alloc_mod(d); + list_add_tail(&mod->entry, &d->mod_list); + mod->data = d; + mod->id = *ptr; ptr++; + while (ptr < endptr) { + if (*(uint8_t *)ptr == 0) { + ptr += 4; + break; + } + + if (!internal && ptr[1]) { + ptr += 4 + *(uint16_t *)(ptr + 2); + continue; + } + + tag = fs_alloc_tag(d, 0); + tag->id = *ptr; ptr++; + tag->internal = (*ptr & 0x01) ? 1 : 0; ptr ++; + tag->size = *(uint16_t *)ptr; ptr += 2; + tag->data = ptr; ptr += tag->size; + + list_add_tail(&tag->entry, &mod->tag_list); + } + } + + backup_restore_session(d); + + return; + +out: + munmap(ptr, st.st_size); + close(fd); +} + +static void fs_restore(int internal) +{ + DIR *dirp; + struct dirent ent, *res; + + if (!conf_path) + return; + + dirp = opendir(conf_path); + if (!dirp) { + log_emerg("backup_file: opendir: %s\n", strerror(errno)); + return; + } + + while (1) { + if (readdir_r(dirp, &ent, &res)) { + log_emerg("backup_file: readdir: %s\n", strerror(errno)); + break; + } + if (!res) + break; + if (strcmp(ent.d_name, ".") == 0 || strcmp(ent.d_name, "..") == 0) + continue; + restore_session(ent.d_name, internal); + } + + closedir(dirp); +} + +static struct backup_storage file_storage = { + .create = fs_create, + .commit = fs_commit, + .free = fs_free, + .alloc_mod = fs_alloc_mod, + .free_mod = fs_free_mod, + .add_fd = fs_add_fd, + .alloc_tag = fs_alloc_tag, + .free_tag = fs_free_tag, + .restore = fs_restore, +}; + +static void init(void) +{ + conf_path = conf_get_opt("backup", "path"); + + backup_register_storage(&file_storage); +} + +DEFINE_INIT(1000, init); |