示例#1
0
static ssize_t RangeSinkWrite(const uint8_t* data, ssize_t size, void* token) {
    RangeSinkState* rss = (RangeSinkState*) token;

    if (rss->p_remain <= 0) {
        fprintf(stderr, "range sink write overrun");
        exit(1);
    }

    ssize_t written = 0;
    while (size > 0) {
        size_t write_now = size;
        if (rss->p_remain < write_now) write_now = rss->p_remain;
        writeblock(rss->fd, data, write_now);
        data += write_now;
        size -= write_now;

        rss->p_remain -= write_now;
        written += write_now;

        if (rss->p_remain == 0) {
            // move to the next block
            ++rss->p_block;
            if (rss->p_block < rss->tgt->count) {
                rss->p_remain = (rss->tgt->pos[rss->p_block*2+1] - rss->tgt->pos[rss->p_block*2]) * BLOCKSIZE;
                check_lseek(rss->fd, (off64_t)rss->tgt->pos[rss->p_block*2] * BLOCKSIZE, SEEK_SET);
            } else {
                // we can't write any more; return how many bytes have
                // been written so far.
                return written;
            }
        }
    }

    return written;
}
示例#2
0
Value* RangeSha1Fn(const char* name, State* state, int argc, Expr* argv[]) {
    Value* blockdev_filename;
    Value* ranges;
    const uint8_t* digest = NULL;
    if (ReadValueArgs(state, argv, 2, &blockdev_filename, &ranges) < 0) {
        return NULL;
    }

    if (blockdev_filename->type != VAL_STRING) {
        ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
        goto done;
    }
    if (ranges->type != VAL_STRING) {
        ErrorAbort(state, "ranges argument to %s must be string", name);
        goto done;
    }

    int fd = open(blockdev_filename->data, O_RDWR);
    if (fd < 0) {
        ErrorAbort(state, "failed to open %s: %s", blockdev_filename->data, strerror(errno));
        goto done;
    }

    RangeSet* rs = parse_range(ranges->data);
    uint8_t buffer[BLOCKSIZE];

    SHA_CTX ctx;
    SHA_init(&ctx);

    int i, j;
    for (i = 0; i < rs->count; ++i) {
        check_lseek(fd, (off64_t)rs->pos[i*2] * BLOCKSIZE, SEEK_SET);
        for (j = rs->pos[i*2]; j < rs->pos[i*2+1]; ++j) {
            readblock(fd, buffer, BLOCKSIZE);
            SHA_update(&ctx, buffer, BLOCKSIZE);
        }
    }
    digest = SHA_final(&ctx);
    close(fd);

  done:
    FreeValue(blockdev_filename);
    FreeValue(ranges);
    if (digest == NULL) {
        return StringValue(strdup(""));
    } else {
        return StringValue(PrintSha1(digest));
    }
}
示例#3
0
int
main(int argc, char *argv[])
{
	char dir_path[PATH_MAX], file_path[PATH_MAX];
	int dummy, fd;
	size_t size;

	aio_present = 0;
	size = sizeof(dummy);
	if (sysctlbyname("vfs.aio", &dummy, &size, NULL, 0) < 0) {
		if (errno == EISDIR)
			aio_present = 1;
	}

	strlcpy(dir_path, "/tmp/open-dir.XXXXXXXXXXX", sizeof(dir_path));
	if (mkdtemp(dir_path) == NULL)
		err(-1, "mkdtemp");
	if (chmod(dir_path, PERM_DIR) < 0) {
		warn("chmod %s", dir_path);
		(void)rmdir(dir_path);
		exit(-1);
	}
	strlcpy(file_path, "/tmp/open-file.XXXXXXXXXXX", sizeof(file_path));
	fd = mkstemp(file_path);
	if (fd < 0) {
		warn("mkstemp");
		(void)rmdir(dir_path);
		exit(-1);
	}
	close(fd);
	if (chmod(file_path, PERM_FILE) < 0) {
		warn("chmod %s", file_path);
		(void)unlink(file_path);
		(void)rmdir(dir_path);
		exit(-1);
	}
	check_directory_open_modes(dir_path, file_modes, file_modes_count);

	check_dup("check_dup_dir", dir_path, dir_modes, dir_modes_count);
	check_dup("check_dup_file", file_path, file_modes, file_modes_count);

	check_dup2("check_dup2_dir", dir_path, dir_modes, dir_modes_count);
	check_dup2("check_dup2_file", file_path, file_modes,
	    file_modes_count);

	check_fchdir("check_fchdir", dir_path, dir_modes, dir_modes_count);

	check_fchflags("check_fchflags_dir", dir_path, dir_modes,
	    dir_modes_count);
	check_fchflags("check_fchflags_file", file_path, file_modes,
	    file_modes_count);

	check_fchmod("check_fchmod_dir", dir_path, PERM_DIR, dir_modes,
	    dir_modes_count);
	check_fchmod("check_fchmod_file", file_path, PERM_FILE, file_modes,
	    file_modes_count);

	check_fchown("check_fchown_dir", dir_path, dir_modes,
	    dir_modes_count);
	check_fchown("check_fchown_file", file_path, file_modes,
	    file_modes_count);

	check_flock("check_flock_dir", dir_path, dir_modes, dir_modes_count);
	check_flock("check_flock_file", file_path, file_modes,
	    file_modes_count);

	check_fpathconf("check_fpathconf_dir", dir_path, dir_modes,
	    dir_modes_count);
	check_fpathconf("check_fpathconf_file", file_path, file_modes,
	    file_modes_count);

	check_fstat("check_fstat_dir", dir_path, dir_modes, dir_modes_count);
	check_fstat("check_fstat_file", file_path, file_modes,
	    file_modes_count);

	check_fstatfs("check_fstatfs_dir", dir_path, dir_modes,
	    dir_modes_count);
	check_fstatfs("check_fstatfs_file", file_path, file_modes,
	    file_modes_count);

	check_fsync("check_fsync_dir", dir_path, dir_modes, dir_modes_count);
	check_fsync("check_fsync_file", file_path, file_modes,
	    file_modes_count);

	check_ftruncate("check_ftruncate_dir", dir_path, dir_modes,
	    dir_modes_count);
	check_ftruncate("check_ftruncate_file", file_path, file_modes,
	    file_modes_count);

	check_futimes("check_futimes_dir", dir_path, dir_modes,
	    dir_modes_count);
	check_futimes("check_futimes_file", file_path, file_modes,
	    file_modes_count);

	check_lseek("check_lseek_dir", dir_path, dir_modes, dir_modes_count);
	check_lseek("check_lseek_file", file_path, file_modes,
	    file_modes_count);

	check_getdents("check_getdents_dir", dir_path, 1, dir_modes,
	    dir_modes_count);
	check_getdents("check_getdents_file", file_path, 0, file_modes,
	    file_modes_count);

	check_sendfile("check_sendfile_dir", dir_path, 1, dir_modes,
	    dir_modes_count);
	check_sendfile("check_sendfile_file", file_path, 0, file_modes,
	    file_modes_count);

	check_write("check_write_dir", write, dir_path, dir_modes,
	    dir_modes_count);
	check_write("check_write_file", write, file_path, file_modes,
	    file_modes_count);

	check_write("check_writev_dir", writev_wrapper, dir_path, dir_modes,
	    dir_modes_count);
	check_write("check_writev_file", writev_wrapper, file_path,
	    file_modes, file_modes_count);

	check_write("check_pwrite_dir", pwrite_wrapper, dir_path, dir_modes,
	    dir_modes_count);
	check_write("check_pwrite_file", pwrite_wrapper, file_path,
	    file_modes, file_modes_count);

	check_write("check_pwritev_dir", pwritev_wrapper, dir_path,
	    dir_modes, dir_modes_count);
	check_write("check_pwritev_file", pwritev_wrapper, file_path,
	    file_modes, file_modes_count);

	if (aio_present) {
		check_write("check_aio_write_dir", aio_write_wrapper,
		    dir_path, dir_modes, dir_modes_count);
		check_write("check_aio_write_file", aio_write_wrapper,
		    file_path, file_modes, file_modes_count);
	}

	check_read("check_read_dir", read, dir_path, dir_modes,
	    dir_modes_count);
	check_read("check_read_file", read, file_path, file_modes,
	    file_modes_count);

	check_read("check_readv_dir", readv_wrapper, dir_path, dir_modes,
	    dir_modes_count);
	check_read("check_readv_file", readv_wrapper, file_path,
	    file_modes, file_modes_count);

	check_read("check_pread_dir", pread_wrapper, dir_path, dir_modes,
	    dir_modes_count);
	check_read("check_pread_file", pread_wrapper, file_path,
	    file_modes, file_modes_count);

	check_read("check_preadv_dir", preadv_wrapper, dir_path,
	    dir_modes, dir_modes_count);
	check_read("check_preadv_file", preadv_wrapper, file_path,
	    file_modes, file_modes_count);

	if (aio_present) {
		check_read("check_aio_read_dir", aio_read_wrapper, dir_path,
		    dir_modes, dir_modes_count);
		check_read("check_aio_read_file", aio_read_wrapper,
		    file_path, file_modes, file_modes_count);
	}

	check_mmap_read("check_mmap_read_dir", dir_path, 1, dir_modes,
	    dir_modes_count);
	check_mmap_read("check_mmap_read_file", file_path, 0, file_modes,
	    file_modes_count);

	check_mmap_write("check_mmap_write_dir", dir_path, dir_modes,
	    dir_modes_count);
	check_mmap_write("check_mmap_write_file", file_path, file_modes,
	    file_modes_count);

	check_mmap_exec("check_mmap_exec_dir", dir_path, 1, dir_modes,
	    dir_modes_count);
	check_mmap_exec("check_mmap_exec_file", file_path, 0, file_modes,
	    file_modes_count);

	check_mmap_write_private("check_mmap_write_private_dir", dir_path, 1,
	    dir_modes, dir_modes_count);
	check_mmap_write_private("check_mmap_write_private_file", file_path,
	    0, file_modes, file_modes_count);

	(void)unlink(file_path);
	(void)rmdir(dir_path);
	exit(0);
}
示例#4
0
Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) {
    Value* blockdev_filename;
    Value* transfer_list_value;
    char* transfer_list = NULL;
    Value* new_data_fn;
    Value* patch_data_fn;
    bool success = false;

    if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list_value,
                      &new_data_fn, &patch_data_fn) < 0) {
        return NULL;
    }

    if (blockdev_filename->type != VAL_STRING) {
        ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
        goto done;
    }
    if (transfer_list_value->type != VAL_BLOB) {
        ErrorAbort(state, "transfer_list argument to %s must be blob", name);
        goto done;
    }
    if (new_data_fn->type != VAL_STRING) {
        ErrorAbort(state, "new_data_fn argument to %s must be string", name);
        goto done;
    }
    if (patch_data_fn->type != VAL_STRING) {
        ErrorAbort(state, "patch_data_fn argument to %s must be string", name);
        goto done;
    }

    UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
    FILE* cmd_pipe = ui->cmd_pipe;

    ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;

    const ZipEntry* patch_entry = mzFindZipEntry(za, patch_data_fn->data);
    if (patch_entry == NULL) {
        ErrorAbort(state, "%s(): no file \"%s\" in package", name, patch_data_fn->data);
        goto done;
    }

    uint8_t* patch_start = ((UpdaterInfo*)(state->cookie))->package_zip_addr +
        mzGetZipEntryOffset(patch_entry);

    const ZipEntry* new_entry = mzFindZipEntry(za, new_data_fn->data);
    if (new_entry == NULL) {
        ErrorAbort(state, "%s(): no file \"%s\" in package", name, new_data_fn->data);
        goto done;
    }

    // The transfer list is a text file containing commands to
    // transfer data from one place to another on the target
    // partition.  We parse it and execute the commands in order:
    //
    //    zero [rangeset]
    //      - fill the indicated blocks with zeros
    //
    //    new [rangeset]
    //      - fill the blocks with data read from the new_data file
    //
    //    bsdiff patchstart patchlen [src rangeset] [tgt rangeset]
    //    imgdiff patchstart patchlen [src rangeset] [tgt rangeset]
    //      - read the source blocks, apply a patch, write result to
    //        target blocks.  bsdiff or imgdiff specifies the type of
    //        patch.
    //
    //    move [src rangeset] [tgt rangeset]
    //      - copy data from source blocks to target blocks (no patch
    //        needed; rangesets are the same size)
    //
    //    erase [rangeset]
    //      - mark the given blocks as empty
    //
    // The creator of the transfer list will guarantee that no block
    // is read (ie, used as the source for a patch or move) after it
    // has been written.
    //
    // Within one command the source and target ranges may overlap so
    // in general we need to read the entire source into memory before
    // writing anything to the target blocks.
    //
    // All the patch data is concatenated into one patch_data file in
    // the update package.  It must be stored uncompressed because we
    // memory-map it in directly from the archive.  (Since patches are
    // already compressed, we lose very little by not compressing
    // their concatenation.)

    pthread_t new_data_thread;
    NewThreadInfo nti;
    nti.za = za;
    nti.entry = new_entry;
    nti.rss = NULL;
    pthread_mutex_init(&nti.mu, NULL);
    pthread_cond_init(&nti.cv, NULL);

    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    pthread_create(&new_data_thread, &attr, unzip_new_data, &nti);

    int i, j;

    char* linesave;
    char* wordsave;

    int fd = open(blockdev_filename->data, O_RDWR);
    if (fd < 0) {
        ErrorAbort(state, "failed to open %s: %s", blockdev_filename->data, strerror(errno));
        goto done;
    }

    char* line;
    char* word;

    // The data in transfer_list_value is not necessarily
    // null-terminated, so we need to copy it to a new buffer and add
    // the null that strtok_r will need.
    transfer_list = malloc(transfer_list_value->size+1);
    if (transfer_list == NULL) {
        fprintf(stderr, "failed to allocate %zd bytes for transfer list\n",
                transfer_list_value->size+1);
        exit(1);
    }
    memcpy(transfer_list, transfer_list_value->data, transfer_list_value->size);
    transfer_list[transfer_list_value->size] = '\0';

    line = strtok_r(transfer_list, "\n", &linesave);

    // first line in transfer list is the version number; currently
    // there's only version 1.
    if (strcmp(line, "1") != 0) {
        ErrorAbort(state, "unexpected transfer list version [%s]\n", line);
        goto done;
    }

    // second line in transfer list is the total number of blocks we
    // expect to write.
    line = strtok_r(NULL, "\n", &linesave);
    int total_blocks = strtol(line, NULL, 0);
    // shouldn't happen, but avoid divide by zero.
    if (total_blocks == 0) ++total_blocks;
    int blocks_so_far = 0;

    uint8_t* buffer = NULL;
    size_t buffer_alloc = 0;

    // third and subsequent lines are all individual transfer commands.
    for (line = strtok_r(NULL, "\n", &linesave); line;
         line = strtok_r(NULL, "\n", &linesave)) {
        char* style;
        style = strtok_r(line, " ", &wordsave);

        if (strcmp("move", style) == 0) {
            word = strtok_r(NULL, " ", &wordsave);
            RangeSet* src = parse_range(word);
            word = strtok_r(NULL, " ", &wordsave);
            RangeSet* tgt = parse_range(word);

            printf("  moving %d blocks\n", src->size);

            allocate(src->size * BLOCKSIZE, &buffer, &buffer_alloc);
            size_t p = 0;
            for (i = 0; i < src->count; ++i) {
                check_lseek(fd, (off64_t)src->pos[i*2] * BLOCKSIZE, SEEK_SET);
                size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE;
                readblock(fd, buffer+p, sz);
                p += sz;
            }

            p = 0;
            for (i = 0; i < tgt->count; ++i) {
                check_lseek(fd, (off64_t)tgt->pos[i*2] * BLOCKSIZE, SEEK_SET);
                size_t sz = (tgt->pos[i*2+1] - tgt->pos[i*2]) * BLOCKSIZE;
                writeblock(fd, buffer+p, sz);
                p += sz;
            }

            blocks_so_far += tgt->size;
            fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks);
            fflush(cmd_pipe);

            free(src);
            free(tgt);

        } else if (strcmp("zero", style) == 0 ||
                   (DEBUG_ERASE && strcmp("erase", style) == 0)) {
            word = strtok_r(NULL, " ", &wordsave);
            RangeSet* tgt = parse_range(word);

            printf("  zeroing %d blocks\n", tgt->size);

            allocate(BLOCKSIZE, &buffer, &buffer_alloc);
            memset(buffer, 0, BLOCKSIZE);
            for (i = 0; i < tgt->count; ++i) {
                check_lseek(fd, (off64_t)tgt->pos[i*2] * BLOCKSIZE, SEEK_SET);
                for (j = tgt->pos[i*2]; j < tgt->pos[i*2+1]; ++j) {
                    writeblock(fd, buffer, BLOCKSIZE);
                }
            }

            if (style[0] == 'z') {   // "zero" but not "erase"
                blocks_so_far += tgt->size;
                fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks);
                fflush(cmd_pipe);
            }

            free(tgt);
        } else if (strcmp("new", style) == 0) {

            word = strtok_r(NULL, " ", &wordsave);
            RangeSet* tgt = parse_range(word);

            printf("  writing %d blocks of new data\n", tgt->size);

            RangeSinkState rss;
            rss.fd = fd;
            rss.tgt = tgt;
            rss.p_block = 0;
            rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE;
            check_lseek(fd, (off64_t)tgt->pos[0] * BLOCKSIZE, SEEK_SET);

            pthread_mutex_lock(&nti.mu);
            nti.rss = &rss;
            pthread_cond_broadcast(&nti.cv);
            while (nti.rss) {
                pthread_cond_wait(&nti.cv, &nti.mu);
            }
            pthread_mutex_unlock(&nti.mu);

            blocks_so_far += tgt->size;
            fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks);
            fflush(cmd_pipe);

            free(tgt);

        } else if (strcmp("bsdiff", style) == 0 ||
                   strcmp("imgdiff", style) == 0) {
            word = strtok_r(NULL, " ", &wordsave);
            size_t patch_offset = strtoul(word, NULL, 0);
            word = strtok_r(NULL, " ", &wordsave);
            size_t patch_len = strtoul(word, NULL, 0);

            word = strtok_r(NULL, " ", &wordsave);
            RangeSet* src = parse_range(word);
            word = strtok_r(NULL, " ", &wordsave);
            RangeSet* tgt = parse_range(word);

            printf("  patching %d blocks to %d\n", src->size, tgt->size);

            // Read the source into memory.
            allocate(src->size * BLOCKSIZE, &buffer, &buffer_alloc);
            size_t p = 0;
            for (i = 0; i < src->count; ++i) {
                check_lseek(fd, (off64_t)src->pos[i*2] * BLOCKSIZE, SEEK_SET);
                size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE;
                readblock(fd, buffer+p, sz);
                p += sz;
            }

            Value patch_value;
            patch_value.type = VAL_BLOB;
            patch_value.size = patch_len;
            patch_value.data = (char*)(patch_start + patch_offset);

            RangeSinkState rss;
            rss.fd = fd;
            rss.tgt = tgt;
            rss.p_block = 0;
            rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE;
            check_lseek(fd, (off64_t)tgt->pos[0] * BLOCKSIZE, SEEK_SET);

            if (style[0] == 'i') {      // imgdiff
                ApplyImagePatch(buffer, src->size * BLOCKSIZE,
                                &patch_value,
                                &RangeSinkWrite, &rss, NULL, NULL);
            } else {
                ApplyBSDiffPatch(buffer, src->size * BLOCKSIZE,
                                 &patch_value, 0,
                                 &RangeSinkWrite, &rss, NULL);
            }

            // We expect the output of the patcher to fill the tgt ranges exactly.
            if (rss.p_block != tgt->count || rss.p_remain != 0) {
                fprintf(stderr, "range sink underrun?\n");
            }

            blocks_so_far += tgt->size;
            fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks);
            fflush(cmd_pipe);

            free(src);
            free(tgt);
        } else if (!DEBUG_ERASE && strcmp("erase", style) == 0) {
            struct stat st;
            if (fstat(fd, &st) == 0 && S_ISBLK(st.st_mode)) {
                word = strtok_r(NULL, " ", &wordsave);
                RangeSet* tgt = parse_range(word);

                printf("  erasing %d blocks\n", tgt->size);

                for (i = 0; i < tgt->count; ++i) {
                    uint64_t range[2];
                    // offset in bytes
                    range[0] = tgt->pos[i*2] * (uint64_t)BLOCKSIZE;
                    // len in bytes
                    range[1] = (tgt->pos[i*2+1] - tgt->pos[i*2]) * (uint64_t)BLOCKSIZE;

                    if (ioctl(fd, BLKDISCARD, &range) < 0) {
                        printf("    blkdiscard failed: %s\n", strerror(errno));
                    }
                }

                free(tgt);
            } else {
                printf("  ignoring erase (not block device)\n");
            }
        } else {
            fprintf(stderr, "unknown transfer style \"%s\"\n", style);
            exit(1);
        }
    }

    pthread_join(new_data_thread, NULL);
    success = true;

    free(buffer);
    printf("wrote %d blocks; expected %d\n", blocks_so_far, total_blocks);
    printf("max alloc needed was %zu\n", buffer_alloc);

  done:
    free(transfer_list);
    FreeValue(blockdev_filename);
    FreeValue(transfer_list_value);
    FreeValue(new_data_fn);
    FreeValue(patch_data_fn);
    return StringValue(success ? strdup("t") : strdup(""));
}