static int hammer_ioc_get_data(hammer_transaction_t trans, hammer_inode_t ip, struct hammer_ioc_data *data) { struct hammer_cursor cursor; int bytes; int error; /* XXX cached inode ? */ error = hammer_init_cursor(trans, &cursor, NULL, NULL); if (error) goto failed; cursor.key_beg = data->elm; cursor.flags |= HAMMER_CURSOR_BACKEND; error = hammer_btree_lookup(&cursor); if (error == 0) { error = hammer_btree_extract_data(&cursor); if (error == 0) { data->leaf = *cursor.leaf; bytes = cursor.leaf->data_len; if (bytes > data->size) bytes = data->size; error = copyout(cursor.data, data->ubuf, bytes); } } failed: hammer_done_cursor(&cursor); return (error); }
/* * Reblock a record's data. Both the B-Tree element and record pointers * to the data must be adjusted. */ static int hammer_reblock_data(struct hammer_ioc_reblock *reblock, hammer_cursor_t cursor, hammer_btree_elm_t elm) { hammer_buffer_t data_buffer = NULL; hammer_off_t odata_offset; hammer_off_t ndata_offset; int error; void *ndata; error = hammer_btree_extract_data(cursor); if (error) return (error); ndata = hammer_alloc_data(cursor->trans, elm->leaf.data_len, elm->leaf.base.rec_type, &ndata_offset, &data_buffer, 0, &error); if (error) goto done; hammer_io_notmeta(data_buffer); /* * Move the data. Note that we must invalidate any cached * data buffer in the cursor before calling blockmap_free. * The blockmap_free may free up the entire big-block and * will not be able to invalidate it if the cursor is holding * a data buffer cached in that big-block. */ hammer_modify_buffer_noundo(cursor->trans, data_buffer); bcopy(cursor->data, ndata, elm->leaf.data_len); hammer_modify_buffer_done(data_buffer); hammer_cursor_invalidate_cache(cursor); hammer_blockmap_free(cursor->trans, elm->leaf.data_offset, elm->leaf.data_len); hammer_modify_node(cursor->trans, cursor->node, &elm->leaf.data_offset, sizeof(hammer_off_t)); odata_offset = elm->leaf.data_offset; elm->leaf.data_offset = ndata_offset; hammer_modify_node_done(cursor->node); if (hammer_debug_general & 0x4000) { hdkprintf("%08x %016jx -> %016jx\n", (elm ? elm->base.localization : -1), (intmax_t)odata_offset, (intmax_t)ndata_offset); } done: if (data_buffer) hammer_rel_buffer(data_buffer, 0); return (error); }
/* * Retrieve the PFS hammer cleanup utility config record. This is * different (newer than) the PFS config. */ static int hammer_ioc_get_config(hammer_transaction_t trans, hammer_inode_t ip, struct hammer_ioc_config *config) { struct hammer_cursor cursor; int error; error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL); if (error) { hammer_done_cursor(&cursor); return(error); } cursor.key_beg.obj_id = HAMMER_OBJID_ROOT; cursor.key_beg.create_tid = 0; cursor.key_beg.delete_tid = 0; cursor.key_beg.obj_type = 0; cursor.key_beg.rec_type = HAMMER_RECTYPE_CONFIG; cursor.key_beg.localization = ip->obj_localization | HAMMER_LOCALIZE_INODE; cursor.key_beg.key = 0; /* config space page 0 */ cursor.asof = HAMMER_MAX_TID; cursor.flags |= HAMMER_CURSOR_ASOF; error = hammer_btree_lookup(&cursor); if (error == 0) { error = hammer_btree_extract_data(&cursor); if (error == 0) config->config = cursor.data->config; } /* error can be ENOENT */ config->head.error = error; hammer_done_cursor(&cursor); return(0); }
/* * All B-Tree records within the specified key range which also conform * to the transaction id range are returned. Mirroring code keeps track * of the last transaction id fully scanned and can efficiently pick up * where it left off if interrupted. * * The PFS is identified in the mirror structure. The passed ip is just * some directory in the overall HAMMER filesystem and has nothing to * do with the PFS. */ int hammer_ioc_mirror_read(hammer_transaction_t trans, hammer_inode_t ip, struct hammer_ioc_mirror_rw *mirror) { struct hammer_cmirror cmirror; struct hammer_cursor cursor; union hammer_ioc_mrecord_any mrec; hammer_btree_leaf_elm_t elm; char *uptr; int error; int data_len; int bytes; int eatdisk; int mrec_flags; uint32_t localization; hammer_crc_t rec_crc; localization = pfs_to_lo(mirror->pfs_id); if ((mirror->key_beg.localization | mirror->key_end.localization) & HAMMER_LOCALIZE_PSEUDOFS_MASK) { return(EINVAL); } if (hammer_btree_cmp(&mirror->key_beg, &mirror->key_end) > 0) return(EINVAL); mirror->key_cur = mirror->key_beg; mirror->key_cur.localization &= HAMMER_LOCALIZE_MASK; mirror->key_cur.localization |= localization; bzero(&mrec, sizeof(mrec)); bzero(&cmirror, sizeof(cmirror)); /* * Make CRC errors non-fatal (at least on data), causing an EDOM * error instead of EIO. */ trans->flags |= HAMMER_TRANSF_CRCDOM; retry: error = hammer_init_cursor(trans, &cursor, NULL, NULL); if (error) { hammer_done_cursor(&cursor); goto failed; } cursor.key_beg = mirror->key_cur; cursor.key_end = mirror->key_end; cursor.key_end.localization &= HAMMER_LOCALIZE_MASK; cursor.key_end.localization |= localization; cursor.flags |= HAMMER_CURSOR_END_INCLUSIVE; cursor.flags |= HAMMER_CURSOR_BACKEND; /* * This flag filters the search to only return elements whos create * or delete TID is >= mirror_tid. The B-Tree uses the mirror_tid * field stored with internal and leaf nodes to shortcut the scan. */ cursor.flags |= HAMMER_CURSOR_MIRROR_FILTERED; cursor.cmirror = &cmirror; cmirror.mirror_tid = mirror->tid_beg; error = hammer_btree_first(&cursor); while (error == 0) { /* * Yield to more important tasks */ if (error == 0) { error = hammer_signal_check(trans->hmp); if (error) break; } /* * An internal node can be returned in mirror-filtered * mode and indicates that the scan is returning a skip * range in the cursor->cmirror structure. */ uptr = (char *)mirror->ubuf + mirror->count; if (cursor.node->ondisk->type == HAMMER_BTREE_TYPE_INTERNAL) { /* * Check space */ mirror->key_cur = cmirror.skip_beg; bytes = sizeof(mrec.skip); if (mirror->count + HAMMER_HEAD_DOALIGN(bytes) > mirror->size) { break; } /* * Fill mrec */ mrec.head.signature = HAMMER_IOC_MIRROR_SIGNATURE; mrec.head.type = HAMMER_MREC_TYPE_SKIP; mrec.head.rec_size = bytes; mrec.skip.skip_beg = cmirror.skip_beg; mrec.skip.skip_end = cmirror.skip_end; hammer_crc_set_mrec_head(&mrec.head, bytes); error = copyout(&mrec, uptr, bytes); eatdisk = 0; goto didwrite; } /* * Leaf node. In full-history mode we could filter out * elements modified outside the user-requested TID range. * * However, such elements must be returned so the writer * can compare them against the target to determine what * needs to be deleted on the target, particular for * no-history mirrors. */ KKASSERT(cursor.node->ondisk->type == HAMMER_BTREE_TYPE_LEAF); elm = &cursor.node->ondisk->elms[cursor.index].leaf; mirror->key_cur = elm->base; /* * If the record was created after our end point we just * ignore it. */ if (elm->base.create_tid > mirror->tid_end) { error = 0; bytes = 0; eatdisk = 1; goto didwrite; } /* * Determine if we should generate a PASS or a REC. PASS * records are records without any data payload. Such * records will be generated if the target is already expected * to have the record, allowing it to delete the gaps. * * A PASS record is also used to perform deletions on the * target. * * Such deletions are needed if the master or files on the * master are no-history, or if the slave is so far behind * the master has already been pruned. */ if (elm->base.create_tid < mirror->tid_beg) { bytes = sizeof(mrec.rec); if (mirror->count + HAMMER_HEAD_DOALIGN(bytes) > mirror->size) { break; } /* * Fill mrec. */ mrec.head.signature = HAMMER_IOC_MIRROR_SIGNATURE; mrec.head.type = HAMMER_MREC_TYPE_PASS; mrec.head.rec_size = bytes; mrec.rec.leaf = *elm; hammer_crc_set_mrec_head(&mrec.head, bytes); error = copyout(&mrec, uptr, bytes); eatdisk = 1; goto didwrite; } /* * The core code exports the data to userland. * * CRC errors on data are reported but passed through, * but the data must be washed by the user program. * * If userland just wants the btree records it can * request that bulk data not be returned. This is * use during mirror-stream histogram generation. */ mrec_flags = 0; data_len = (elm->data_offset) ? elm->data_len : 0; if (data_len && (mirror->head.flags & HAMMER_IOC_MIRROR_NODATA)) { data_len = 0; mrec_flags |= HAMMER_MRECF_NODATA; } if (data_len) { error = hammer_btree_extract_data(&cursor); if (error) { if (error != EDOM) break; mrec_flags |= HAMMER_MRECF_CRC_ERROR | HAMMER_MRECF_DATA_CRC_BAD; } } bytes = sizeof(mrec.rec) + data_len; if (mirror->count + HAMMER_HEAD_DOALIGN(bytes) > mirror->size) break; /* * Construct the record for userland and copyout. * * The user is asking for a snapshot, if the record was * deleted beyond the user-requested ending tid, the record * is not considered deleted from the point of view of * userland and delete_tid is cleared. */ mrec.head.signature = HAMMER_IOC_MIRROR_SIGNATURE; mrec.head.type = HAMMER_MREC_TYPE_REC | mrec_flags; mrec.head.rec_size = bytes; mrec.rec.leaf = *elm; if (elm->base.delete_tid > mirror->tid_end) mrec.rec.leaf.base.delete_tid = 0; rec_crc = hammer_crc_get_mrec_head(&mrec.head, sizeof(mrec.rec)); if (data_len) rec_crc = crc32_ext(cursor.data, data_len, rec_crc); mrec.head.rec_crc = rec_crc; error = copyout(&mrec, uptr, sizeof(mrec.rec)); if (data_len && error == 0) { error = copyout(cursor.data, uptr + sizeof(mrec.rec), data_len); } eatdisk = 1; /* * eatdisk controls whether we skip the current cursor * position on the next scan or not. If doing a SKIP * the cursor is already positioned properly for the next * scan and eatdisk will be 0. */ didwrite: if (error == 0) { mirror->count += HAMMER_HEAD_DOALIGN(bytes); if (eatdisk) cursor.flags |= HAMMER_CURSOR_ATEDISK; else cursor.flags &= ~HAMMER_CURSOR_ATEDISK; error = hammer_btree_iterate(&cursor); } } if (error == ENOENT) { mirror->key_cur = mirror->key_end; error = 0; } hammer_done_cursor(&cursor); if (error == EDEADLK) goto retry; if (error == EINTR) { mirror->head.flags |= HAMMER_IOC_HEAD_INTR; error = 0; } failed: mirror->key_cur.localization &= HAMMER_LOCALIZE_MASK; return(error); }
/* * Retrieve the PFS hammer cleanup utility config record. This is * different (newer than) the PFS config. * * This is kinda a hack. */ static int hammer_ioc_set_config(hammer_transaction_t trans, hammer_inode_t ip, struct hammer_ioc_config *config) { struct hammer_btree_leaf_elm leaf; struct hammer_cursor cursor; hammer_mount_t hmp = ip->hmp; int error; again: error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL); if (error) { hammer_done_cursor(&cursor); return(error); } bzero(&leaf, sizeof(leaf)); leaf.base.obj_id = HAMMER_OBJID_ROOT; leaf.base.rec_type = HAMMER_RECTYPE_CONFIG; leaf.base.create_tid = hammer_alloc_tid(hmp, 1); leaf.base.btype = HAMMER_BTREE_TYPE_RECORD; leaf.base.localization = ip->obj_localization | HAMMER_LOCALIZE_INODE; leaf.base.key = 0; /* page 0 */ leaf.data_len = sizeof(struct hammer_config_data); cursor.key_beg = leaf.base; cursor.asof = HAMMER_MAX_TID; cursor.flags |= HAMMER_CURSOR_BACKEND | HAMMER_CURSOR_ASOF; error = hammer_btree_lookup(&cursor); if (error == 0) { error = hammer_btree_extract_data(&cursor); error = hammer_delete_at_cursor(&cursor, HAMMER_DELETE_DESTROY, 0, 0, 0, NULL); if (error == EDEADLK) { hammer_done_cursor(&cursor); goto again; } } if (error == ENOENT) error = 0; if (error == 0) { /* * NOTE: Must reload key_beg after an ASOF search because * the create_tid may have been modified during the * search. */ cursor.flags &= ~HAMMER_CURSOR_ASOF; cursor.key_beg = leaf.base; error = hammer_create_at_cursor(&cursor, &leaf, &config->config, HAMMER_CREATE_MODE_SYS); if (error == EDEADLK) { hammer_done_cursor(&cursor); goto again; } } config->head.error = error; hammer_done_cursor(&cursor); return(0); }
/* * Retrieve as many snapshot ids as possible or until the array is * full, starting after the last transaction id passed in. If count * is 0 we retrieve starting at the beginning. * * NOTE: Because the b-tree key field is signed but transaction ids * are unsigned the returned list will be signed-sorted instead * of unsigned sorted. The Caller must still sort the aggregate * results. */ static int hammer_ioc_get_snapshot(hammer_transaction_t trans, hammer_inode_t ip, struct hammer_ioc_snapshot *snap) { struct hammer_cursor cursor; int error; /* * Validate structure */ if (snap->index != 0) return (EINVAL); if (snap->count > HAMMER_SNAPS_PER_IOCTL) return (EINVAL); /* * Look for keys starting after the previous iteration, or at * the beginning if snap->count is 0. */ error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL); if (error) { hammer_done_cursor(&cursor); return(error); } cursor.key_beg.obj_id = HAMMER_OBJID_ROOT; cursor.key_beg.create_tid = 0; cursor.key_beg.delete_tid = 0; cursor.key_beg.obj_type = 0; cursor.key_beg.rec_type = HAMMER_RECTYPE_SNAPSHOT; cursor.key_beg.localization = ip->obj_localization | HAMMER_LOCALIZE_INODE; if (snap->count == 0) cursor.key_beg.key = HAMMER_MIN_KEY; else cursor.key_beg.key = (int64_t)snap->snaps[snap->count - 1].tid + 1; cursor.key_end = cursor.key_beg; cursor.key_end.key = HAMMER_MAX_KEY; cursor.asof = HAMMER_MAX_TID; cursor.flags |= HAMMER_CURSOR_END_EXCLUSIVE | HAMMER_CURSOR_ASOF; snap->count = 0; error = hammer_btree_first(&cursor); while (error == 0 && snap->count < HAMMER_SNAPS_PER_IOCTL) { error = hammer_btree_extract_leaf(&cursor); if (error) break; if (cursor.leaf->base.rec_type == HAMMER_RECTYPE_SNAPSHOT) { error = hammer_btree_extract_data(&cursor); snap->snaps[snap->count] = cursor.data->snap; /* * The snap data tid should match the key but might * not due to a bug in the HAMMER v3 conversion code. * * This error will work itself out over time but we * have to force a match or the snapshot will not * be deletable. */ if (cursor.data->snap.tid != (hammer_tid_t)cursor.leaf->base.key) { hkprintf("lo=%08x snapshot key " "0x%016jx data mismatch 0x%016jx\n", cursor.key_beg.localization, (uintmax_t)cursor.data->snap.tid, cursor.leaf->base.key); hkprintf("Probably left over from the " "original v3 conversion, hammer " "cleanup should get it eventually\n"); snap->snaps[snap->count].tid = cursor.leaf->base.key; } ++snap->count; } error = hammer_btree_iterate(&cursor); } if (error == ENOENT) { snap->head.flags |= HAMMER_IOC_SNAPSHOT_EOF; error = 0; } snap->head.error = error; hammer_done_cursor(&cursor); return(0); }