/* * Write a single entry to the archive. */ static void write_entry(struct bsdtar *bsdtar, struct archive *a, struct archive_entry *entry) { int fd = -1; int e; size_t align = 4096; if (archive_entry_size(entry) > 0) { const char *pathname = archive_entry_sourcepath(entry); /* TODO: Use O_DIRECT here and set 'align' to the * actual filesystem block size. As of July 2010, new * directory-traversal code is going in that will make * it much easier to track filesystem properties like * this during the traversal. */ fd = open(pathname, O_RDONLY | O_BINARY); align = 4096; if (fd == -1) { if (!bsdtar->verbose) lafe_warnc(errno, "%s: could not open file", pathname); else fprintf(stderr, ": %s", strerror(errno)); return; } } e = archive_write_header(a, entry); if (e != ARCHIVE_OK) { if (!bsdtar->verbose) lafe_warnc(0, "%s: %s", archive_entry_pathname(entry), archive_error_string(a)); else fprintf(stderr, ": %s", archive_error_string(a)); } if (e == ARCHIVE_FATAL) exit(1); /* * If we opened a file earlier, write it out now. Note that * the format handler might have reset the size field to zero * to inform us that the archive body won't get stored. In * that case, just skip the write. */ if (e >= ARCHIVE_WARN && fd >= 0 && archive_entry_size(entry) > 0) { if (write_file_data(bsdtar, a, entry, fd, align)) exit(1); } /* * If we opened a file, close it now even if there was an error * which made us decide not to write the archive body. */ if (fd >= 0) close(fd); }
/* * Backend for write_entry. */ static void write_entry_backend(struct bsdtar *bsdtar, struct archive *a, struct archive_entry *entry) { int fd = -1; int e; if (archive_entry_size(entry) > 0) { const char *pathname = archive_entry_sourcepath(entry); fd = open(pathname, O_RDONLY); if (fd == -1) { if (!bsdtar->verbose) bsdtar_warnc(bsdtar, errno, "%s: could not open file", pathname); else fprintf(stderr, ": %s", strerror(errno)); return; } } e = archive_write_header(a, entry); if (e != ARCHIVE_OK) { if (!bsdtar->verbose) bsdtar_warnc(bsdtar, 0, "%s: %s", archive_entry_pathname(entry), archive_error_string(a)); else fprintf(stderr, ": %s", archive_error_string(a)); } if (e == ARCHIVE_FATAL) exit(1); /* * If we opened a file earlier, write it out now. Note that * the format handler might have reset the size field to zero * to inform us that the archive body won't get stored. In * that case, just skip the write. */ if (e >= ARCHIVE_WARN && fd >= 0 && archive_entry_size(entry) > 0) { if (write_file_data(bsdtar, a, entry, fd)) exit(1); } /* * If we opened a file, close it now even if there was an error * which made us decide not to write the archive body. */ if (fd >= 0) close(fd); }
static void write_fs_h(node * n, FILE * f, int add) { unsigned zero = 0; int i = 0; seek_to_node(f, n->node_ptr); fseek(f, add, SEEK_CUR); fputc(n->type, f); switch (n->type) { case NT_FILE: fputc(n->u.file.perm, f); fwrite((char*)(&zero), 1, 2, f); fwrite((char*)(&n->u.file.owner), sizeof(int), 1, f); if (n->u.file.name == NULL) fwrite((char*)(&zero), sizeof(int), 1, f); else fwrite((char*)(&n->u.file.name->node_ptr), sizeof(int), 1, f); if (n->u.file.first_data == NULL) fwrite((char*)(&zero), sizeof(int), 1, f); else fwrite((char*)(&n->u.file.first_data->node_ptr), sizeof(int), 1, f); if (n->u.file.next == NULL) fwrite((char*)(&zero), sizeof(int), 1, f); else fwrite((char*)(&n->u.file.next->node_ptr), sizeof(int), 1, f); if (n->u.file.parent == NULL) fwrite((char*)(&zero), sizeof(int), 1, f); else fwrite((char*)(&n->u.file.parent->node_ptr), sizeof(int), 1, f); if (n->u.file.name != NULL) write_fs_h(n->u.file.name, f, add); if (n->u.file.first_data != NULL) write_fs_h(n->u.file.first_data, f, add); if (n->u.file.next != NULL) write_fs_h(n->u.file.next, f, add); if (add == 0) write_file_data(n, f); break; case NT_DIR: fputc(n->u.dir.perm, f); fwrite((char*)(&zero), 1, 2, f); fwrite((char*)(&n->u.dir.owner), sizeof(int), 1, f); if (n->u.dir.name == NULL) fwrite((char*)(&zero), sizeof(int), 1, f); else fwrite((char*)(&n->u.dir.name->node_ptr), sizeof(int), 1, f); if (n->u.dir.first_file == NULL) fwrite((char*)(&zero), sizeof(int), 1, f); else fwrite((char*)(&n->u.dir.first_file->node_ptr), sizeof(int), 1, f); if (n->u.dir.next == NULL) fwrite((char*)(&zero), sizeof(int), 1, f); else fwrite((char*)(&n->u.dir.next->node_ptr), sizeof(int), 1, f); if (n->u.dir.parent == NULL) fwrite((char*)(&zero), sizeof(int), 1, f); else fwrite((char*)(&n->u.dir.parent->node_ptr), sizeof(int), 1, f); if (n->u.dir.name != NULL) write_fs_h(n->u.dir.name, f, add); if (n->u.dir.first_file != NULL) write_fs_h(n->u.dir.first_file, f, add); if (n->u.dir.next != NULL) write_fs_h(n->u.dir.next, f, add); break; case NT_INDIRECT: fputc(n->u.indirect.perm, f); fwrite((char*)(&zero), 1, 2, f); fwrite((char*)(&n->u.indirect.owner), sizeof(int), 1, f); if (n->u.indirect.name == NULL) fwrite((char*)(&zero), sizeof(int), 1, f); else fwrite((char*)(&n->u.indirect.name->node_ptr), sizeof(int), 1, f); if (n->u.indirect.dest == NULL) fwrite((char*)(&zero), sizeof(int), 1, f); else fwrite((char*)(&n->u.indirect.dest->node_ptr), sizeof(int), 1, f); if (n->u.indirect.next == NULL) fwrite((char*)(&zero), sizeof(int), 1, f); else fwrite((char*)(&n->u.indirect.next->node_ptr), sizeof(int), 1, f); if (n->u.indirect.parent == NULL) fwrite((char*)(&zero), sizeof(int), 1, f); else fwrite((char*)(&n->u.indirect.parent->node_ptr), sizeof(int), 1, f); if (n->u.indirect.name != NULL) write_fs_h(n->u.indirect.name, f, add); if (n->u.indirect.dest != NULL) write_fs_h(n->u.indirect.dest, f, add); if (n->u.indirect.next != NULL) write_fs_h(n->u.indirect.next, f, add); break; case NT_DATA: fwrite((char*)(&zero), 1, 3, f); fwrite((char*)(&n->u.data.block_count), sizeof(int), 1, f); fwrite((char*)(&n->u.data.lba), sizeof(long long), 1, f); if (n->u.data.next == NULL) fwrite((char*)(&zero), sizeof(int), 1, f); else fwrite((char*)(&n->u.data.next), sizeof(int), 1, f); fwrite((char*)(&n->u.data.bytes_in_last_block), sizeof(int), 1, f); if (n->u.data.next != NULL) write_fs_h(n->u.data.next, f, add); break; case NT_STRING: fputc(n->u.string.size, f); for (i = 0; i < STR_CHARS_PER_NODE; i++) fputc(n->u.string.str[i], f); if (n->u.string.next == NULL) fwrite((char*)(&zero), sizeof(int), 1, f); else fwrite((char*)(&n->u.string.next), sizeof(int), 1, f); if (n->u.string.next != NULL) write_fs_h(n->u.string.next, f, add); break; case NT_FREE: fseek(f, 23, SEEK_CUR); break; } if (add == 0) { write_fs_h(n, f, 0x1000); } }
/* * Backend for write_entry. */ static void write_entry_backend(struct bsdtar *bsdtar, struct archive *a, struct archive_entry *entry, const struct stat *st, const char *rpath) { off_t skiplen; CCACHE_ENTRY *cce = NULL; int filecached = 0; int fd = -1; int e; /* * If this archive entry needs data, we have a canonical path to the * relevant file, and the chunkification cache isn't disabled, ask * the chunkification cache to find the entry for the file (if one * already exists) and tell us if it can provide the entire file. */ if ((st != NULL) && S_ISREG(st->st_mode) && (rpath != NULL) && (archive_entry_size(entry) > 0) && (bsdtar->cachecrunch < 2) && (bsdtar->chunk_cache != NULL)) { cce = ccache_entry_lookup(bsdtar->chunk_cache, rpath, st, bsdtar->write_cookie, &filecached); } /* * Open the file if we need to write archive entry data and the * chunkification cache can't provide all of it. */ if ((archive_entry_size(entry) > 0) && (filecached == 0)) { const char *pathname = archive_entry_sourcepath(entry); fd = open(pathname, O_RDONLY); if (fd == -1) { if (!bsdtar->verbose) bsdtar_warnc(bsdtar, errno, "%s: could not open file", pathname); else fprintf(stderr, ": %s", strerror(errno)); return; } } /* Write the archive header. */ if (MODE_HEADER(bsdtar, a)) { bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); exit(1); } e = archive_write_header(a, entry); if (e != ARCHIVE_OK) { if (!bsdtar->verbose) bsdtar_warnc(bsdtar, 0, "%s: %s", archive_entry_pathname(entry), archive_error_string(a)); else fprintf(stderr, ": %s", archive_error_string(a)); } if (e == ARCHIVE_FATAL) exit(1); /* * If we opened a file earlier, write it out now. Note that * the format handler might have reset the size field to zero * to inform us that the archive body won't get stored. In * that case, just skip the write. */ /* If the cache can provide the entire archive entry, do it. */ if (e >= ARCHIVE_WARN && filecached && archive_entry_size(entry) > 0) { if (MODE_DATA(bsdtar, a)) { bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); exit(1); } skiplen = ccache_entry_write(cce, bsdtar->write_cookie); if (skiplen < st->st_size) { bsdtar_warnc(bsdtar, 0, "Error writing cached archive entry"); exit(1); } if (archive_write_skip(a, skiplen)) { bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); exit(1); } } /* * We don't need to write anything now if the file was cached * and the cache wrote it out earlier. */ if (e >= ARCHIVE_WARN && fd >= 0 && archive_entry_size(entry) > 0 && filecached == 0) { /* Switch into data mode. */ if (MODE_DATA(bsdtar, a)) { bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); exit(1); } if (cce != NULL) { /* Ask the cache to write as much as possible. */ skiplen = ccache_entry_writefile(cce, bsdtar->write_cookie, bsdtar->cachecrunch, fd); if (skiplen < 0) { bsdtar_warnc(bsdtar, 0, "Error writing archive"); exit(1); } /* Skip forward in the file. */ if (lseek(fd, skiplen, SEEK_SET) == -1) { bsdtar_warnc(bsdtar, errno, "lseek(%s)", archive_entry_pathname(entry)); exit(1); } /* Tell the archive layer that we've skipped. */ if (archive_write_skip(a, skiplen)) { bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); exit(1); } } if (write_file_data(bsdtar, a, entry, fd)) exit(1); } /* This entry is done. */ if (!truncate_archive(bsdtar) && MODE_DONE(bsdtar, a)) { bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); exit(1); } /* Tell the cache that we're done. */ if (cce != NULL) { if (ccache_entry_end(bsdtar->chunk_cache, cce, bsdtar->write_cookie, rpath, bsdtar->snaptime)) exit(1); cce = NULL; } /* * If we opened a file, close it now even if there was an error * which made us decide not to write the archive body. */ if (fd >= 0) close(fd); }
static bool update_remote_files(void *param, obs_data_t *remote_file) { struct update_info *info = param; struct file_update_data data = { .name = obs_data_get_string(remote_file, "name"), .version = (int)obs_data_get_int(remote_file, "version") }; enum_files(info->cache_package, newer_than_cache, &data); if (!data.newer && data.found) return true; if (!do_relative_http_request(info, info->remote_url, data.name)) return true; if (info->callback) { struct file_download_data download_data; bool confirm; download_data.name = data.name; download_data.version = data.version; download_data.buffer.da = info->file_data.da; confirm = info->callback(info->param, &download_data); info->file_data.da = download_data.buffer.da; if (!confirm) { info("Update file '%s' (version %d) rejected", data.name, data.version); return true; } } write_file_data(info, info->temp, data.name); replace_file(info->temp, info->cache, data.name); info("Successfully updated file '%s' (version %d)", data.name, data.version); return true; } static void update_save_metadata(struct update_info *info) { struct dstr path = { 0 }; if (!info->etag_remote) return; dstr_copy(&path, info->cache); dstr_cat(&path, "meta.json"); obs_data_t *data; data = obs_data_create(); obs_data_set_string(data, "etag", info->etag_remote); obs_data_save_json(data, path.array); obs_data_release(data); } static void update_remote_version(struct update_info *info, int cur_version) { int remote_version; long response_code; if (!do_http_request(info, info->url, &response_code)) return; if (response_code == 304) return; if (!info->file_data.array || info->file_data.array[0] != '{') { warn("Remote package does not exist or is not valid json"); return; } update_save_metadata(info); info->remote_package = obs_data_create_from_json( (char*)info->file_data.array); if (!info->remote_package) { warn("Failed to initialize remote package json"); return; } remote_version = (int)obs_data_get_int(info->remote_package, "version"); if (remote_version <= cur_version) return; write_file_data(info, info->temp, "package.json"); info->remote_url = obs_data_get_string(info->remote_package, "url"); if (!info->remote_url) { warn("No remote url in package file"); return; } /* download new files */ enum_files(info->remote_package, update_remote_files, info); replace_file(info->temp, info->cache, "package.json"); info("Successfully updated package (version %d)", remote_version); return; }