/* * Write out a new record. */ static int hammer_mirror_write(hammer_cursor_t cursor, struct hammer_ioc_mrecord_rec *mrec, char *udata) { hammer_transaction_t trans; hammer_buffer_t data_buffer; hammer_off_t ndata_offset; hammer_tid_t high_tid; void *ndata; int error; int doprop; trans = cursor->trans; data_buffer = NULL; /* * Get the sync lock so the whole mess is atomic */ hammer_sync_lock_sh(trans); /* * Allocate and adjust data */ if (mrec->leaf.data_len && mrec->leaf.data_offset) { ndata = hammer_alloc_data(trans, mrec->leaf.data_len, mrec->leaf.base.rec_type, &ndata_offset, &data_buffer, 0, &error); if (ndata == NULL) return(error); mrec->leaf.data_offset = ndata_offset; hammer_modify_buffer(trans, data_buffer, NULL, 0); error = copyin(udata, ndata, mrec->leaf.data_len); if (error == 0) { if (hammer_crc_test_leaf(ndata, &mrec->leaf) == 0) { kprintf("data crc mismatch on pipe\n"); error = EINVAL; } else { error = hammer_mirror_localize_data( ndata, &mrec->leaf); } } hammer_modify_buffer_done(data_buffer); } else { mrec->leaf.data_offset = 0; error = 0; ndata = NULL; } if (error) goto failed; /* * Do the insertion. This can fail with a EDEADLK or EALREADY */ cursor->flags |= HAMMER_CURSOR_INSERT; error = hammer_btree_lookup(cursor); if (error != ENOENT) { if (error == 0) error = EALREADY; goto failed; } error = hammer_btree_insert(cursor, &mrec->leaf, &doprop); /* * Cursor is left on the current element, we want to skip it now. */ cursor->flags |= HAMMER_CURSOR_ATEDISK; cursor->flags &= ~HAMMER_CURSOR_INSERT; /* * Track a count of active inodes. */ if (error == 0 && mrec->leaf.base.rec_type == HAMMER_RECTYPE_INODE && mrec->leaf.base.delete_tid == 0) { hammer_modify_volume_field(trans, trans->rootvol, vol0_stat_inodes); ++trans->hmp->rootvol->ondisk->vol0_stat_inodes; hammer_modify_volume_done(trans->rootvol); } /* * vol0_next_tid must track the highest TID stored in the filesystem. * We do not need to generate undo for this update. */ high_tid = mrec->leaf.base.create_tid; if (high_tid < mrec->leaf.base.delete_tid) high_tid = mrec->leaf.base.delete_tid; if (trans->rootvol->ondisk->vol0_next_tid < high_tid) { hammer_modify_volume(trans, trans->rootvol, NULL, 0); trans->rootvol->ondisk->vol0_next_tid = high_tid; hammer_modify_volume_done(trans->rootvol); } /* * WARNING! cursor's leaf pointer may have changed after * do_propagation returns. */ if (error == 0 && doprop) hammer_btree_do_propagation(cursor, NULL, &mrec->leaf); failed: /* * Cleanup */ if (error && mrec->leaf.data_offset) { hammer_blockmap_free(cursor->trans, mrec->leaf.data_offset, mrec->leaf.data_len); } hammer_sync_unlock(trans); if (data_buffer) hammer_rel_buffer(data_buffer, 0); return(error); }
/* * Read and return multiple mrecords */ static int read_mrecords(int fd, char *buf, u_int size, hammer_ioc_mrecord_head_t pickup) { hammer_ioc_mrecord_any_t mrec; u_int count; size_t n; size_t i; size_t bytes; int type; count = 0; while (size - count >= HAMMER_MREC_HEADSIZE) { /* * Cached the record header in case we run out of buffer * space. */ fflush(stdout); if (pickup->signature == 0) { for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) { i = read(fd, (char *)pickup + n, HAMMER_MREC_HEADSIZE - n); if (i <= 0) break; } if (n == 0) break; if (n != HAMMER_MREC_HEADSIZE) { fprintf(stderr, "read_mrecords: short read on pipe\n"); exit(1); } if (pickup->signature != HAMMER_IOC_MIRROR_SIGNATURE) { fprintf(stderr, "read_mrecords: malformed record on pipe, " "bad signature\n"); exit(1); } } if (pickup->rec_size < HAMMER_MREC_HEADSIZE || pickup->rec_size > sizeof(*mrec) + HAMMER_XBUFSIZE) { fprintf(stderr, "read_mrecords: malformed record on pipe, " "illegal rec_size\n"); exit(1); } /* * Stop if we have insufficient space for the record and data. */ bytes = HAMMER_HEAD_DOALIGN(pickup->rec_size); if (size - count < bytes) break; /* * Stop if the record type is not a REC, SKIP, or PASS, * which are the only types the ioctl supports. Other types * are used only by the userland protocol. * * Ignore all flags. */ type = pickup->type & HAMMER_MRECF_TYPE_LOMASK; if (type != HAMMER_MREC_TYPE_PFSD && type != HAMMER_MREC_TYPE_REC && type != HAMMER_MREC_TYPE_SKIP && type != HAMMER_MREC_TYPE_PASS) { break; } /* * Read the remainder and clear the pickup signature. */ for (n = HAMMER_MREC_HEADSIZE; n < bytes; n += i) { i = read(fd, buf + count + n, bytes - n); if (i <= 0) break; } if (n != bytes) { fprintf(stderr, "read_mrecords: short read on pipe\n"); exit(1); } bcopy(pickup, buf + count, HAMMER_MREC_HEADSIZE); pickup->signature = 0; pickup->type = 0; mrec = (void *)(buf + count); /* * Validate the completed record */ if (!hammer_crc_test_mrec_head(&mrec->head, mrec->head.rec_size)) { fprintf(stderr, "read_mrecords: malformed record " "on pipe, bad crc\n"); exit(1); } /* * If its a B-Tree record validate the data crc. * * NOTE: If the VFS passes us an explicitly errorde mrec * we just pass it through. */ type = mrec->head.type & HAMMER_MRECF_TYPE_MASK; if (type == HAMMER_MREC_TYPE_REC) { if (mrec->head.rec_size < sizeof(mrec->rec) + mrec->rec.leaf.data_len) { fprintf(stderr, "read_mrecords: malformed record on " "pipe, illegal element data_len\n"); exit(1); } if (mrec->rec.leaf.data_len && mrec->rec.leaf.data_offset && hammer_crc_test_leaf(&mrec->rec + 1, &mrec->rec.leaf) == 0) { fprintf(stderr, "read_mrecords: data_crc did not " "match data! obj=%016jx key=%016jx\n", (uintmax_t)mrec->rec.leaf.base.obj_id, (uintmax_t)mrec->rec.leaf.base.key); fprintf(stderr, "continuing, but there are problems\n"); } } count += bytes; } return(count); }