int sentinel_write(struct volume *v, uint32_t _seq) { int ret, block; struct stat s; uint32_t seq; if (stat("/tmp/config.tar.gz", &s)) { ULOG_ERR("failed to stat /tmp/config.tar.gz\n"); return -1; } snapshot_next_free(v, &seq); if (_seq) seq = _seq; block = v->size / v->block_size; block -= pad_file_size(v, s.st_size) / v->block_size; if (block < 0) block = 0; ret = snapshot_write_file(v, block, "/tmp/config.tar.gz", seq, CONF); if (ret) ULOG_ERR("failed to write sentinel\n"); else ULOG_INFO("wrote /tmp/config.tar.gz sentinel\n"); return ret; }
int snapshot_read_file(struct volume *v, int block, char *file, uint32_t type) { struct file_header hdr; char buffer[256]; int out, offset = 0; if (volume_read(v, &hdr, block * v->block_size, sizeof(struct file_header))) { ULOG_ERR("failed to read header\n"); return -1; } be32_to_hdr(&hdr); if (hdr.magic != OWRT) return -1; if (hdr.type != type) return -1; if (valid_file_size(hdr.length)) return -1; out = open(file, O_WRONLY | O_CREAT, 0700); if (!out) { ULOG_ERR("failed to open %s\n", file); return -1; } offset = block * v->block_size + sizeof(hdr); while (hdr.length > 0) { int len = sizeof(buffer); if (hdr.length < len) len = hdr.length; if (volume_read(v, buffer, offset, len)) return -1; if (write(out, buffer, len) != len) return -1; offset += len; hdr.length -= len; } close(out); if (verify_file_hash(file, hdr.md5)) { ULOG_ERR("md5 verification failed\n"); unlink(file); return 0; } block += pad_file_size(v, hdr.length) / v->block_size; return block; }
int snapshot_write_file(struct volume *v, int block, char *file, uint32_t seq, uint32_t type) { uint32_t md5[4] = { 0 }; struct file_header hdr; struct stat s; char buffer[256]; int in = 0, len, offset; int ret = -1; if (stat(file, &s) || md5sum(file, md5)) { ULOG_ERR("stat failed on %s\n", file); goto out; } if ((block * v->block_size) + pad_file_size(v, s.st_size) > v->size) { ULOG_ERR("upgrade is too big for the flash\n"); goto out; } volume_erase(v, block * v->block_size, pad_file_size(v, s.st_size)); volume_erase(v, block * v->block_size + pad_file_size(v, s.st_size), v->block_size); hdr.length = s.st_size; hdr.magic = OWRT; hdr.type = type; hdr.seq = seq; memcpy(hdr.md5, md5, sizeof(md5)); hdr_to_be32(&hdr); if (volume_write(v, &hdr, block * v->block_size, sizeof(struct file_header))) { ULOG_ERR("failed to write header\n"); goto out; } in = open(file, O_RDONLY); if (in < 1) { ULOG_ERR("failed to open %s\n", file); goto out; } offset = (block * v->block_size) + sizeof(struct file_header); while ((len = read(in, buffer, sizeof(buffer))) > 0) { if (volume_write(v, buffer, offset, len) < 0) goto out; offset += len; } ret = 0; out: if (in > 0) close(in); return ret; }
int config_find(struct volume *v, struct file_header *conf, struct file_header *sentinel) { uint32_t seq; int i, next = snapshot_next_free(v, &seq); conf->magic = sentinel->magic = 0; if (!volume_read(v, conf, next, sizeof(*conf))) be32_to_hdr(conf); for (i = (v->size / v->block_size) - 1; i > 0; i--) { if (volume_read(v, sentinel, i * v->block_size, sizeof(*sentinel))) { ULOG_ERR("failed to read header\n"); return -1; } be32_to_hdr(sentinel); if (sentinel->magic == OWRT && sentinel->type == CONF && !valid_file_size(sentinel->length)) { if (next == i) return -1; return i; } } return -1; }
int snapshot_next_free(struct volume *v, uint32_t *seq) { struct file_header hdr = { 0 }; int block = 0; *seq = rand(); do { if (volume_read(v, &hdr, block * v->block_size, sizeof(struct file_header))) { ULOG_ERR("scanning for next free block failed\n"); return 0; } be32_to_hdr(&hdr); if (hdr.magic != OWRT) break; if (hdr.type == DATA && !valid_file_size(hdr.length)) { if (*seq + 1 != hdr.seq && block) return block; *seq = hdr.seq; block += pad_file_size(v, hdr.length) / v->block_size; } } while (hdr.type == DATA); return block; }
int verify_file_hash(char *file, uint32_t *hash) { uint32_t md5[4]; if (md5sum(file, md5)) { ULOG_ERR("failed to generate md5 sum\n"); return -1; } if (memcmp(md5, hash, sizeof(md5))) { ULOG_ERR("failed to verify hash of %s.\n", file); return -1; } return 0; }
static int snapshot_sync(struct volume *v) { struct file_header sentinel, conf; int next, block = 0; uint32_t seq; next = snapshot_next_free(v, &seq); block = config_find(v, &conf, &sentinel); if (is_config(&conf) && conf.seq != seq) { conf.magic = 0; volume_erase(v, next * v->block_size, 2 * v->block_size); } if (is_config(&sentinel) && (sentinel.seq != seq)) { sentinel.magic = 0; volume_erase(v, block * v->block_size, v->block_size); } if (!is_config(&conf) && !is_config(&sentinel)) { // ULOG_ERR("no config found\n"); } else if (((is_config(&conf) && is_config(&sentinel)) && (memcmp(conf.md5, sentinel.md5, sizeof(conf.md5)) || (conf.seq != sentinel.seq))) || (is_config(&conf) && !is_config(&sentinel))) { uint32_t seq; int next = snapshot_next_free(v, &seq); int ret = snapshot_read_file(v, next, "/tmp/config.tar.gz", CONF); if (ret > 0) { if (sentinel_write(v, conf.seq)) ULOG_ERR("failed to write sentinel data"); } } else if (!is_config(&conf) && is_config(&sentinel) && next) { int ret = snapshot_read_file(v, block, "/tmp/config.tar.gz", CONF); if (ret > 0) if (volatile_write(v, sentinel.seq)) ULOG_ERR("failed to write sentinel data"); } else ULOG_INFO("config in sync\n"); unlink("/tmp/config.tar.gz"); return 0; }
int main(int argc, char **argv) { struct volume *v; int ch, yes = 0, reset = 0; while ((ch = getopt(argc, argv, "yr")) != -1) { switch(ch) { case 'y': yes = 1; break; case 'r': reset = 1; break; } } if (!yes && ask_user()) return -1; /* * TODO: Currently this only checks if kernel supports OverlayFS. We * should check if there is a mount point using it with rootfs_data * as upperdir. */ if (find_filesystem("overlay")) { ULOG_ERR("overlayfs not supported by kernel\n"); return -1; } v = volume_find("rootfs_data"); if (!v) { ULOG_ERR("MTD partition 'rootfs_data' not found\n"); return -1; } volume_init(v); if (!strcmp(*argv, "jffs2mark")) return jffs2_mark(v); return jffs2_reset(v, reset); }
static int jffs2_mark(struct volume *v) { __u32 deadc0de = __cpu_to_be32(0xdeadc0de); size_t sz; int fd; fd = open(v->blk, O_WRONLY); ULOG_INFO("%s will be erased on next mount\n", v->blk); if (!fd) { ULOG_ERR("opening %s failed\n", v->blk); return -1; } sz = write(fd, &deadc0de, sizeof(deadc0de)); close(fd); if (sz != 4) { ULOG_ERR("writing %s failed: %s\n", v->blk, strerror(errno)); return -1; } return 0; }
json_object *hostspec_collect_block_device() { json_object *obj = json_object_new_object(); assert(obj); DIR *dir = opendir("/sys/block"); if (!dir) { ULOG_ERR("Unable to open /sys/block: %s\n", strerror(errno)); goto error; } struct dirent *ent; while ((ent = readdir(dir))) { char const *name = ent->d_name; if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { continue; } json_object *obj_dev = json_object_new_object(); assert(obj_dev); json_object_object_add(obj, name, obj_dev); char buf[1024], *p; if ((p = fgets_close(buf, sizeof buf, fopenf("r", "/sys/block/%s/size", name)))) { int64_t size; if (json_parse_int64(p, &size) == 0) { json_object_object_add(obj_dev, "size", json_object_new_int64(size)); } } if ((p = fgets_close(buf, sizeof buf, fopenf("r", "/sys/block/%s/removable", name)))) { int64_t removable; if (json_parse_int64(p, &removable) == 0) { json_object_object_add(obj_dev, "removable", json_object_new_int64(removable)); } } } closedir(dir); return obj; error: if (dir) { closedir(dir); } json_object_put(obj); return NULL; }
int volatile_write(struct volume *v, uint32_t _seq) { int block, ret; uint32_t seq; block = snapshot_next_free(v, &seq); if (_seq) seq = _seq; if (block < 0) block = 0; ret = snapshot_write_file(v, block, "/tmp/config.tar.gz", seq, CONF); if (ret) ULOG_ERR("failed to write /tmp/config.tar.gz\n"); else ULOG_INFO("wrote /tmp/config.tar.gz\n"); return ret; }
json_object *hostspec_collect_cpu() { json_object *obj = json_object_new_array(); assert(obj); FILE *fp = fopen("/proc/cpuinfo", "r"); if (!fp) { ULOG_ERR("Unable to open /proc/cpuinfo: %s\n", strerror(errno)); goto error; } char buf[1024], *p; json_object *obj_proc = NULL; while ((p = fgets(buf, sizeof buf, fp))) { chomp(p); if (begin_with(p, "processor\t")) { obj_proc = json_object_new_object(); json_object_array_add(obj, obj_proc); } // Only send the model name for now: this should work for x86/amd64/arm/mips if (obj_proc) { if (begin_with(p, "cpu model\t") || begin_with(p, "model name\t")) { json_object_object_add(obj_proc, "model_name", json_object_new_string(after_colon(p))); } } } fclose(fp); return obj; error: if (fp) { fclose(fp); } json_object_put(obj); return NULL; }
static int ubi_volume_match(struct volume *v, char *name, int ubi_num, int volid) { char voldir[BUFLEN], volblkdev[BUFLEN], *volname; struct ubi_priv *p; snprintf(voldir, sizeof(voldir), "%s/ubi%u/ubi%u_%u", ubi_dir_name, ubi_num, ubi_num, volid); snprintf(volblkdev, sizeof(volblkdev), "/dev/ubiblock%u_%u", ubi_num, volid); /* skip if ubiblock device exists */ if (test_open(volblkdev)) return -1; /* todo: skip existing gluebi device for legacy support */ volname = read_string_from_file(voldir, "name"); if (!volname) { ULOG_ERR("Couldn't read %s/name\n", voldir); return -1; } if (strncmp(name, volname, strlen(volname) + 1)) return -1; p = calloc(1, sizeof(struct ubi_priv)); if (!p) return -1; v->priv = p; v->drv = &ubi_driver; p->ubi_num = ubi_num; p->ubi_volid = volid; return ubi_volume_init(v); }