summaryrefslogtreecommitdiff
path: root/accel-pppd/backup/backup_file.c
diff options
context:
space:
mode:
Diffstat (limited to 'accel-pppd/backup/backup_file.c')
-rw-r--r--accel-pppd/backup/backup_file.c351
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);