char * gfs_pio_truncate(GFS_File gf, file_offset_t length) { char *e; gfarm_timerval_t t1, t2; gfs_profile(gfarm_gettimerval(&t1)); e = gfs_pio_check_view_default(gf); if (e != NULL) goto finish; CHECK_WRITABLE(gf); gf->mode &= ~GFS_FILE_MODE_CALC_DIGEST; if (gf->mode & GFS_FILE_MODE_BUFFER_DIRTY) { e = gfs_pio_flush(gf); if (e != NULL) goto finish; } gf->error = NULL; /* purge EOF/error state */ gfs_pio_purge(gf); e = (*gf->ops->view_ftruncate)(gf, length); if (e != NULL) gf->error = e; finish: gfs_profile(gfarm_gettimerval(&t2)); gfs_profile(gfs_pio_truncate_time += gfarm_timerval_sub(&t2, &t1)); return (e); }
char * gfs_pio_write(GFS_File gf, const void *buffer, int size, int *np) { char *e; size_t written; gfarm_timerval_t t1, t2; gfs_profile(gfarm_gettimerval(&t1)); e = gfs_pio_check_view_default(gf); if (e != NULL) return (e); CHECK_WRITABLE(gf); if (size + gf->p > GFS_FILE_BUFSIZE) { /* * gf->buffer[gf->p .. GFS_FILE_BUFSIZE-1] will be overridden * by buffer. */ gf->length = gf->p; e = gfs_pio_flush(gf); if (e != NULL) goto finish; } if (size >= GFS_FILE_BUFSIZE) { /* shortcut to avoid unnecessary memory copy */ assert(gf->p == 0); /* gfs_pio_flush() was called above */ gf->length = 0; gf->mode &= ~GFS_FILE_MODE_BUFFER_DIRTY; e = do_write(gf, buffer, size, &written); if (e != NULL && written == 0) goto finish; gf->offset += written; *np = written; /* XXX - size_t vs int */ e = NULL; goto finish; } gf->mode |= GFS_FILE_MODE_BUFFER_DIRTY; memcpy(gf->buffer + gf->p, buffer, size); gf->p += size; if (gf->p > gf->length) gf->length = gf->p; *np = size; e = NULL; if (gf->p >= GFS_FILE_BUFSIZE) e = gfs_pio_flush(gf); finish: gfs_profile(gfarm_gettimerval(&t2)); gfs_profile(gfs_pio_write_time += gfarm_timerval_sub(&t2, &t1)); return (e); }
int hivex_commit (hive_h *h, const char *filename, int flags) { int fd; if (flags != 0) { SET_ERRNO (EINVAL, "flags != 0"); return -1; } CHECK_WRITABLE (-1); filename = filename ? : h->filename; #ifdef O_CLOEXEC fd = open (filename, O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY|O_CLOEXEC|O_BINARY, 0666); #else fd = open (filename, O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY|O_BINARY, 0666); #endif if (fd == -1) return -1; #ifndef O_CLOEXEC fcntl (fd, F_SETFD, FD_CLOEXEC); #endif /* Update the header fields. */ uint32_t sequence = le32toh (h->hdr->sequence1); sequence++; h->hdr->sequence1 = htole32 (sequence); h->hdr->sequence2 = htole32 (sequence); /* XXX Ought to update h->hdr->last_modified. */ h->hdr->blocks = htole32 (h->endpages - 0x1000); /* Recompute header checksum. */ uint32_t sum = header_checksum (h); h->hdr->csum = htole32 (sum); DEBUG (2, "hivex_commit: new header checksum: 0x%x", sum); if (full_write (fd, h->addr, h->size) != h->size) { int err = errno; close (fd); errno = err; return -1; } if (close (fd) == -1) return -1; return 0; }
char * gfs_pio_putline(GFS_File gf, const char *s) { char *e = gfs_pio_check_view_default(gf); if (e != NULL) return (e); CHECK_WRITABLE(gf); e = gfs_pio_puts(gf, s); if (e != NULL) return (e); return (gfs_pio_putc(gf, '\n')); }
/* mostly compatible with fgets(3) */ char * gfs_pio_puts(GFS_File gf, const char *s) { char *e = gfs_pio_check_view_default(gf); if (e != NULL) return (e); CHECK_WRITABLE(gf); while (*s != '\0') { char *e = gfs_pio_putc(gf, *(unsigned char *)s); if (e != NULL) return (e); s++; } return (NULL); }
char * gfs_pio_flush(GFS_File gf) { char *e = gfs_pio_check_view_default(gf); size_t written; if (e != NULL) return (e); CHECK_WRITABLE(gf); if ((gf->mode & GFS_FILE_MODE_BUFFER_DIRTY) != 0) { e = do_write(gf, gf->buffer, gf->length, &written); if (e != NULL) return (e); gf->mode &= ~GFS_FILE_MODE_BUFFER_DIRTY; } if (gf->p >= gf->length) gfs_pio_purge(gf); return (NULL); }
char * gfs_pio_putc(GFS_File gf, int c) { char *e = gfs_pio_check_view_default(gf); if (e != NULL) return (e); CHECK_WRITABLE(gf); if (gf->p >= GFS_FILE_BUFSIZE) { char *e = gfs_pio_flush(gf); if (e != NULL) return (e); } gf->mode |= GFS_FILE_MODE_BUFFER_DIRTY; gf->buffer[gf->p++] = c; if (gf->p > gf->length) gf->length = gf->p; if (gf->p >= GFS_FILE_BUFSIZE) return (gfs_pio_flush(gf)); return (NULL); }
int hivex_node_set_values (hive_h *h, hive_node_h node, size_t nr_values, const hive_set_value *values, int flags) { CHECK_WRITABLE (-1); if (!IS_VALID_BLOCK (h, node) || !block_id_eq (h, node, "nk")) { SET_ERRNO (EINVAL, "invalid block or not an 'nk' block"); return -1; } /* Delete all existing values. */ if (delete_values (h, node) == -1) return -1; if (nr_values == 0) return 0; /* Allocate value list node. Value lists have no id field. */ static const char nul_id[2] = { 0, 0 }; size_t seg_len = sizeof (struct ntreg_value_list) + (nr_values - 1) * sizeof (uint32_t); size_t vallist_offs = allocate_block (h, seg_len, nul_id); if (vallist_offs == 0) return -1; struct ntreg_nk_record *nk = (struct ntreg_nk_record *) ((char *) h->addr + node); nk->nr_values = htole32 (nr_values); nk->vallist = htole32 (vallist_offs - 0x1000); size_t i; for (i = 0; i < nr_values; ++i) { /* Allocate vk record to store this (key, value) pair. */ static const char vk_id[2] = { 'v', 'k' }; size_t recoded_name_len; int use_utf16; char* recoded_name = _hivex_encode_string (h, values[i].key, &recoded_name_len, &use_utf16); seg_len = sizeof (struct ntreg_vk_record) + recoded_name_len; size_t vk_offs = allocate_block (h, seg_len, vk_id); if (vk_offs == 0) return -1; /* Recalculate pointers that could have been invalidated by * previous call to allocate_block. */ nk = (struct ntreg_nk_record *) ((char *) h->addr + node); struct ntreg_value_list *vallist = (struct ntreg_value_list *) ((char *) h->addr + vallist_offs); vallist->offset[i] = htole32 (vk_offs - 0x1000); struct ntreg_vk_record *vk = (struct ntreg_vk_record *) ((char *) h->addr + vk_offs); vk->name_len = htole16 (recoded_name_len); memcpy (vk->name, recoded_name, recoded_name_len); free (recoded_name); vk->data_type = htole32 (values[i].t); uint32_t len = values[i].len; if (len <= 4) /* store it inline => set MSB flag */ len |= 0x80000000; vk->data_len = htole32 (len); if (recoded_name_len == 0) vk->flags = 0; else vk->flags = htole16 (!use_utf16); if (values[i].len <= 4) /* store it inline */ memcpy (&vk->data_offset, values[i].value, values[i].len); else { size_t offs = allocate_block (h, values[i].len + 4, nul_id); if (offs == 0) return -1; /* Recalculate pointers that could have been invalidated by * previous call to allocate_block. */ nk = (struct ntreg_nk_record *) ((char *) h->addr + node); /* vallist could be invalid here */ vk = (struct ntreg_vk_record *) ((char *) h->addr + vk_offs); memcpy ((char *) h->addr + offs + 4, values[i].value, values[i].len); vk->data_offset = htole32 (offs - 0x1000); } size_t utf16_len = use_utf16 ? recoded_name_len : recoded_name_len * 2; if (utf16_len > le32toh (nk->max_vk_name_len)) nk->max_vk_name_len = htole32 (utf16_len); if (values[i].len > le32toh (nk->max_vk_data_len)) nk->max_vk_data_len = htole32 (values[i].len); } return 0; }
int hivex_node_delete_child (hive_h *h, hive_node_h node) { CHECK_WRITABLE (-1); if (!IS_VALID_BLOCK (h, node) || !block_id_eq (h, node, "nk")) { SET_ERRNO (EINVAL, "invalid block or not an 'nk' block"); return -1; } if (node == hivex_root (h)) { SET_ERRNO (EINVAL, "cannot delete root node"); return -1; } hive_node_h parent = hivex_node_parent (h, node); if (parent == 0) return -1; /* Delete node and all its children and values recursively. */ static const struct hivex_visitor visitor = { .node_end = delete_node }; if (hivex_visit_node (h, node, &visitor, sizeof visitor, NULL, 0) == -1) return -1; /* Delete the link from parent to child. We need to find the lf/lh * record which contains the offset and remove the offset from that * record, then decrement the element count in that record, and * decrement the overall number of subkeys stored in the parent * node. */ hive_node_h *unused; size_t *blocks; if (_hivex_get_children (h, parent, &unused, &blocks, GET_CHILDREN_NO_CHECK_NK)== -1) return -1; free (unused); size_t i, j; for (i = 0; blocks[i] != 0; ++i) { struct ntreg_hbin_block *block = (struct ntreg_hbin_block *) ((char *) h->addr + blocks[i]); if (block->id[0] == 'l' && (block->id[1] == 'f' || block->id[1] == 'h')) { struct ntreg_lf_record *lf = (struct ntreg_lf_record *) block; size_t nr_subkeys_in_lf = le16toh (lf->nr_keys); for (j = 0; j < nr_subkeys_in_lf; ++j) if (le32toh (lf->keys[j].offset) + 0x1000 == node) { for (; j < nr_subkeys_in_lf - 1; ++j) memcpy (&lf->keys[j], &lf->keys[j+1], sizeof (lf->keys[j])); lf->nr_keys = htole16 (nr_subkeys_in_lf - 1); goto found; } } } free (blocks); SET_ERRNO (ENOTSUP, "could not find parent to child link"); return -1; found:; free (blocks); struct ntreg_nk_record *nk = (struct ntreg_nk_record *) ((char *) h->addr + parent); size_t nr_subkeys_in_nk = le32toh (nk->nr_subkeys); nk->nr_subkeys = htole32 (nr_subkeys_in_nk - 1); DEBUG (2, "updating nr_subkeys in parent 0x%zx to %zu", parent, nr_subkeys_in_nk); return 0; }
hive_node_h hivex_node_add_child (hive_h *h, hive_node_h parent, const char *name) { CHECK_WRITABLE (0); if (!IS_VALID_BLOCK (h, parent) || !block_id_eq (h, parent, "nk")) { SET_ERRNO (EINVAL, "invalid block or not an 'nk' block"); return 0; } if (name == NULL || strlen (name) == 0) { SET_ERRNO (EINVAL, "name is NULL or zero length"); return 0; } if (hivex_node_get_child (h, parent, name) != 0) { SET_ERRNO (EEXIST, "a child with that name exists already"); return 0; } size_t recoded_name_len; int use_utf16 = 0; char *recoded_name = _hivex_encode_string (h, name, &recoded_name_len, &use_utf16); if (recoded_name == NULL) { SET_ERRNO (EINVAL, "malformed name"); return 0; } /* Create the new nk-record. */ static const char nk_id[2] = { 'n', 'k' }; size_t seg_len = sizeof (struct ntreg_nk_record) + recoded_name_len; hive_node_h nkoffset = allocate_block (h, seg_len, nk_id); if (nkoffset == 0) { free (recoded_name); return 0; } DEBUG (2, "allocated new nk-record for child at 0x%zx", nkoffset); struct ntreg_nk_record *nk = (struct ntreg_nk_record *) ((char *) h->addr + nkoffset); if (use_utf16) nk->flags = htole16 (0x0000); else nk->flags = htole16 (0x0020); nk->parent = htole32 (parent - 0x1000); nk->subkey_lf = htole32 (0xffffffff); nk->subkey_lf_volatile = htole32 (0xffffffff); nk->vallist = htole32 (0xffffffff); nk->classname = htole32 (0xffffffff); nk->name_len = htole16 (recoded_name_len); memcpy (nk->name, recoded_name, recoded_name_len); free (recoded_name); /* Inherit parent sk. */ struct ntreg_nk_record *parent_nk = (struct ntreg_nk_record *) ((char *) h->addr + parent); size_t parent_sk_offset = le32toh (parent_nk->sk); parent_sk_offset += 0x1000; if (!IS_VALID_BLOCK (h, parent_sk_offset) || !block_id_eq (h, parent_sk_offset, "sk")) { SET_ERRNO (EFAULT, "parent sk is not a valid block (%zu)", parent_sk_offset); return 0; } struct ntreg_sk_record *sk = (struct ntreg_sk_record *) ((char *) h->addr + parent_sk_offset); sk->refcount = htole32 (le32toh (sk->refcount) + 1); nk->sk = htole32 (parent_sk_offset - 0x1000); /* Inherit parent timestamp. */ nk->timestamp = parent_nk->timestamp; /* What I found out the hard way (not documented anywhere): the * subkeys in lh-records must be kept sorted. If you just add a * subkey in a non-sorted position (eg. just add it at the end) then * Windows won't see the subkey _and_ Windows will corrupt the hive * itself when it modifies or saves it. * * So use get_children() to get a list of intermediate records. * get_children() returns these in reading order (which is sorted), * so we look for the lf/lh/li-records in sequence until we find the * key name just after the one we are inserting, and we insert the * subkey just before it. * * The only other case is the no-subkeys case, where we have to * create a brand new lh-record. */ hive_node_h *unused; size_t *blocks; if (_hivex_get_children (h, parent, &unused, &blocks, 0) == -1) return 0; free (unused); size_t i; size_t nr_subkeys_in_parent_nk = le32toh (parent_nk->nr_subkeys); if (nr_subkeys_in_parent_nk == UINT32_MAX) { free (blocks); SET_ERRNO (EOVERFLOW, "too many subkeys (%zu)", nr_subkeys_in_parent_nk); return 0; } if (nr_subkeys_in_parent_nk == 0) { /* No subkeys case. */ /* Free up any existing intermediate blocks. */ for (i = 0; blocks[i] != 0; ++i) mark_block_unused (h, blocks[i]); size_t lh_offs = new_lh_record (h, name, nkoffset); if (lh_offs == 0) { free (blocks); return 0; } /* Recalculate pointers that could have been invalidated by * previous call to allocate_block (via new_lh_record). */ /* nk could be invalid here */ parent_nk = (struct ntreg_nk_record *) ((char *) h->addr + parent); DEBUG (2, "no keys, allocated new lh-record at 0x%zx", lh_offs); parent_nk->subkey_lf = htole32 (lh_offs - 0x1000); } else { /* Insert subkeys case. */ if (insert_subkey (h, name, parent, nkoffset, blocks) == -1) { free (blocks); return 0; } /* Recalculate pointers that could have been invalidated by * previous call to allocate_block (via new_lh_record). */ /* nk could be invalid here */ parent_nk = (struct ntreg_nk_record *) ((char *) h->addr + parent); } free (blocks); /* Update nr_subkeys in parent nk. */ nr_subkeys_in_parent_nk++; parent_nk->nr_subkeys = htole32 (nr_subkeys_in_parent_nk); /* Update max_subkey_name_len in parent nk. */ size_t utf16_len = use_utf16 ? recoded_name_len : recoded_name_len * 2; if (le16toh (parent_nk->max_subkey_name_len) < utf16_len) parent_nk->max_subkey_name_len = htole16 (utf16_len); return nkoffset; }
/* Allocate a single block, first allocating an hbin (page) at the end * of the current file if necessary. NB. To keep the implementation * simple and more likely to be correct, we do not reuse existing free * blocks. * * seg_len is the size of the block (this INCLUDES the block header). * The header of the block is initialized to -seg_len (negative to * indicate used). id[2] is the block ID (type), eg. "nk" for nk- * record. The block bitmap is updated to show this block as valid. * The rest of the contents of the block will be zero. * * **NB** Because allocate_block may reallocate the memory, all * pointers into the memory become potentially invalid. I really * love writing in C, can't you tell? * * Returns: * > 0 : offset of new block * 0 : error (errno set) */ static size_t allocate_block (hive_h *h, size_t seg_len, const char id[2]) { CHECK_WRITABLE (0); if (seg_len < 4) { /* The caller probably forgot to include the header. Note that * value lists have no ID field, so seg_len == 4 would be possible * for them, albeit unusual. */ SET_ERRNO (ERANGE, "refusing too small allocation (%zu)", seg_len); return 0; } /* Refuse really large allocations. */ if (seg_len > HIVEX_MAX_ALLOCATION) { SET_ERRNO (ERANGE, "refusing too large allocation (%zu)", seg_len); return 0; } /* Round up allocation to multiple of 8 bytes. All blocks must be * on an 8 byte boundary. */ seg_len = (seg_len + 7) & ~7; /* Allocate a new page if necessary. */ if (h->endblocks == 0 || h->endblocks + seg_len > h->endpages) { size_t newendblocks = allocate_page (h, seg_len); if (newendblocks == 0) return 0; h->endblocks = newendblocks; } size_t offset = h->endblocks; DEBUG (2, "new block at 0x%zx, size %zu", offset, seg_len); struct ntreg_hbin_block *blockhdr = (struct ntreg_hbin_block *) ((char *) h->addr + offset); memset (blockhdr, 0, seg_len); blockhdr->seg_len = htole32 (- (int32_t) seg_len); if (id[0] && id[1] && seg_len >= sizeof (struct ntreg_hbin_block)) { blockhdr->id[0] = id[0]; blockhdr->id[1] = id[1]; } BITMAP_SET (h->bitmap, offset); h->endblocks += seg_len; /* If there is space after the last block in the last page, then we * have to put a dummy free block header here to mark the rest of * the page as free. */ ssize_t rem = h->endpages - h->endblocks; if (rem > 0) { DEBUG (2, "marking remainder of page free" " starting at 0x%zx, size %zd", h->endblocks, rem); assert (rem >= 4); blockhdr = (struct ntreg_hbin_block *) ((char *) h->addr + h->endblocks); blockhdr->seg_len = htole32 ((int32_t) rem); } return offset; }