#include #include #include #include #include #include #include #include #define ZEV_FILL_INODE_INFO(name, znode) \ do { \ uint64_t mtime[2], ctime[2]; \ sa_bulk_attr_t bulk[2]; \ int count = 0; \ timestruc_t mtime_s, ctime_s; \ SA_ADD_BULK_ATTR(bulk, count, \ SA_ZPL_MTIME(znode->z_zfsvfs), \ NULL, &mtime, 16); \ SA_ADD_BULK_ATTR(bulk, count, \ SA_ZPL_CTIME(znode->z_zfsvfs), \ NULL, &ctime, 16); \ if ((sa_bulk_lookup(znode->z_sa_hdl, bulk, count)) != 0) { \ zev_queue_error(op, "znode write: " \ "mtime/ctime unavailable"); \ /* continue anyway, use fake data */ \ mtime_s.tv_sec = ctime_s.tv_sec = 0; \ } \ ZFS_TIME_DECODE(&mtime_s, mtime); \ ZFS_TIME_DECODE(&ctime_s, ctime); \ rec->name.ino = znode->z_id; \ rec->name.gen = znode->z_gen; \ rec->name.mtime = mtime_s.tv_sec; \ rec->name.ctime = ctime_s.tv_sec; \ rec->name.size = znode->z_size; \ rec->name.type = znode->z_vnode->v_type; \ rec->name.mode = \ znode->z_mode | VTTOIF(znode->z_vnode->v_type); \ rec->name.links = znode->z_links; \ rec->name.flags = znode->z_pflags & ZFS_XATTR ? \ ZEV_FL_XATTR : 0; \ /* CONSTCOND */ \ } while(0) void zev_zfs_mount_cb(vfs_t *vfs, vnode_t *mpt, char *dataset, boolean_t remount) { int op = ZEV_OP_ZFS_MOUNT; char mountpoint[MAXPATHLEN+1]; int mountpoint_len; int dataset_len; zev_zfs_mount_t *rec; zev_msg_t *msg = NULL; int msg_size; znode_t *zp_root; dmu_tx_t *tx; int error; uint64_t txg; /* * workaround: this callback erronously got called for failed * mount attempts, and then crashed the system. Detect this * from inside this callback as a workaround while the bugfix * in the zfs module is not universally available. */ if (vfs == NULL) return; if (vfs->vfs_data == NULL) return; /* vnodetopath() would deadlock for remounts, and we don't need them. */ if (remount) return; zfsvfs_t *zfsvfs = (zfsvfs_t *)vfs->vfs_data; if (zfsvfs->z_os->os_dsl_dataset->ds_is_snapshot) return; if (zev_skip_pool(zfsvfs->z_os)) return; if (zev_skip_fs(zfsvfs)) return; /* expensive, but we don't have many mount ops. */ if ((vnodetopath(NULL, mpt, mountpoint, sizeof(mountpoint), kcred)) != 0) { zev_queue_error(op, "unresolvable mountpoint, dataset=%s", dataset); return; } if (zfs_zget(zfsvfs, zfsvfs->z_root, &zp_root) != 0) { zev_queue_error(op, "can't get root znode, dataset=%s", dataset); return; } /* get current tgx by adding an empty tx */ tx = dmu_tx_create(zfsvfs->z_os); error = dmu_tx_assign(tx, TXG_WAIT); if (error) { dmu_tx_abort(tx); zev_queue_error(op, "can't create tx, dataset=%s", dataset); return; } txg = dmu_tx_get_txg(tx); dmu_tx_commit(tx); dataset_len = strlen(dataset); mountpoint_len = strlen(mountpoint); msg_size = sizeof(*rec) + dataset_len + 1 + mountpoint_len + 1; msg = zev_alloc(sizeof(*msg) + msg_size); msg->size = msg_size; rec = (zev_zfs_mount_t *)(msg + 1); rec->record_len = msg_size; rec->op = op; rec->op_time = ddi_get_time(); rec->guid = dsl_dataset_phys(zfsvfs->z_os->os_dsl_dataset)->ds_guid; rec->txg = txg; rec->remount = remount; rec->dataset_len = dataset_len; rec->mountpoint_len = mountpoint_len; ZEV_FILL_INODE_INFO(root, zp_root); VN_RELE(ZTOV(zp_root)); (void) memcpy(ZEV_DATASET(rec), dataset, dataset_len + 1); (void) memcpy(ZEV_MOUNTPOINT(rec), mountpoint, mountpoint_len + 1); zev_queue_message(op, msg); } void zev_zfs_umount_cb(vfs_t *vfs) { int op = ZEV_OP_ZFS_UMOUNT; zev_zfs_umount_t *rec; zev_msg_t *msg = NULL; int msg_size; struct vnode *vp; znode_t *zp; dmu_tx_t *tx; int error; uint64_t txg; zfsvfs_t *zfsvfs = (zfsvfs_t *)vfs->vfs_data; if (zfsvfs->z_os->os_dsl_dataset->ds_is_snapshot) return; if (zev_skip_pool(zfsvfs->z_os)) return; if (zev_skip_fs(zfsvfs)) return; /* get current tgx by adding an empty tx */ tx = dmu_tx_create(zfsvfs->z_os); error = dmu_tx_assign(tx, TXG_WAIT); if (error) { dmu_tx_abort(tx); zev_queue_error(op, "can't create tx"); return; } txg = dmu_tx_get_txg(tx); dmu_tx_commit(tx); msg_size = sizeof(*rec); msg = zev_alloc(sizeof(*msg) + msg_size); msg->size = msg_size; rec = (zev_zfs_umount_t *)(msg + 1); rec->record_len = msg_size; rec->op = op; rec->op_time = ddi_get_time(); rec->guid = dsl_dataset_phys(zfsvfs->z_os->os_dsl_dataset)->ds_guid; rec->txg = txg; vp = vfs->vfs_vnodecovered; if (strcmp(vfssw[vp->v_vfsp->vfs_fstype].vsw_name, MNTTYPE_ZFS)) { cmn_err(CE_WARN, "covered inode not on zfs filesystem, " "reporting all-zero inode struct."); memset(&rec->covered, 0, sizeof(rec->covered)); } else { zp = VTOZ(vfs->vfs_vnodecovered); ZEV_FILL_INODE_INFO(covered, zp); } zev_queue_message(op, msg); } void zev_zvol_truncate_cb(char *dataset, objset_t *os, dmu_tx_t *tx, uint64_t off, uint64_t len) { int op = ZEV_OP_ZVOL_TRUNCATE; zev_zvol_truncate_t *rec; zev_msg_t *msg = NULL; int msg_size; int dataset_len; if (zev_skip_pool(os)) return; dataset_len = strlen(dataset); msg_size = sizeof(*rec) + dataset_len + 1; msg = zev_alloc(sizeof(*msg) + msg_size); msg->size = msg_size; rec = (zev_zvol_truncate_t *)(msg + 1); rec->record_len = msg_size; rec->op = op; rec->op_time = ddi_get_time(); rec->guid = dsl_dataset_phys(os->os_dsl_dataset)->ds_guid; rec->txg = dmu_tx_get_txg(tx); rec->offset = off; rec->length = len; rec->dataset_len = dataset_len; (void) memcpy(ZEV_DATASET(rec), dataset, dataset_len + 1); zev_queue_message(op, msg); } void zev_zvol_write_cb(char *dataset, objset_t *os, dmu_tx_t *tx, uint64_t off, uint64_t len) { int op = ZEV_OP_ZVOL_WRITE; zev_zvol_write_t *rec; zev_msg_t *msg = NULL; int msg_size; int dataset_len; if (zev_skip_pool(os)) return; dataset_len = strlen(dataset); msg_size = sizeof(*rec) + dataset_len + 1; msg = zev_alloc(sizeof(*msg) + msg_size); msg->size = msg_size; rec = (zev_zvol_write_t *)(msg + 1); rec->record_len = msg_size; rec->op = op; rec->op_time = ddi_get_time(); rec->guid = dsl_dataset_phys(os->os_dsl_dataset)->ds_guid; rec->txg = dmu_tx_get_txg(tx); rec->offset = off; rec->length = len; rec->dataset_len = dataset_len; (void) memcpy(ZEV_DATASET(rec), dataset, dataset_len + 1); zev_queue_message(op, msg); } void zev_znode_close_after_update_cb(znode_t *zp) { int op = ZEV_OP_ZNODE_CLOSE_AFTER_UPDATE; zev_znode_close_after_update_t *rec; zev_msg_t *msg = NULL; int msg_size; if (zev_skip_pool(zp->z_zfsvfs->z_os)) return; if (zev_skip_fs(zp->z_zfsvfs)) return; msg_size = sizeof(*rec); msg = zev_alloc(sizeof(*msg) + msg_size); msg->size = msg_size; rec = (zev_znode_close_after_update_t *)(msg + 1); rec->record_len = msg_size; rec->op = op; rec->op_time = ddi_get_time(); rec->guid = dsl_dataset_phys(zp->z_zfsvfs->z_os->os_dsl_dataset)->ds_guid; ZEV_FILL_INODE_INFO(file, zp); zev_queue_message(op, msg); } void zev_znode_create_cb(znode_t *dzp, znode_t *zp, dmu_tx_t *tx, char *name, uint64_t txtype) { int op = ZEV_OP_ZNODE_CREATE; zev_znode_create_t *rec; zev_msg_t *msg = NULL; int msg_size; int name_len; if (zev_skip_pool(zp->z_zfsvfs->z_os)) return; if (zev_skip_fs(zp->z_zfsvfs)) return; int type = (int)txtype; switch(type) { case TX_CREATE: case TX_CREATE_ACL: case TX_CREATE_ATTR: case TX_CREATE_ACL_ATTR: op = ZEV_OP_ZNODE_CREATE; break; case TX_MKDIR: case TX_MKDIR_ACL: case TX_MKDIR_ATTR: case TX_MKDIR_ACL_ATTR: op = ZEV_OP_ZNODE_MKDIR; break; case TX_MKXATTR: op = ZEV_OP_ZNODE_MAKE_XATTR_DIR; break; default: zev_queue_error(ZEV_OP_ZNODE_CREATE, "ERROR: ZNODE_CREATE: unknown txtype %d " "(dir_inode=%d:%d inode=%d:%d name='%s')\n", type, dzp->z_gen, dzp->z_id, zp->z_gen, zp->z_id, name); return; } /* all three types use the same struct, so this works for all types: */ name_len = strlen(name); msg_size = sizeof(*rec) + name_len + 1; msg = zev_alloc(sizeof(*msg) + msg_size); msg->size = msg_size; rec = (zev_znode_create_t *)(msg + 1); rec->record_len = msg_size; rec->op = op; rec->op_time = ddi_get_time(); rec->guid = dsl_dataset_phys(zp->z_zfsvfs->z_os->os_dsl_dataset)->ds_guid; rec->txg = dmu_tx_get_txg(tx); ZEV_FILL_INODE_INFO(parent, dzp); ZEV_FILL_INODE_INFO(file, zp); rec->name_len = name_len; (void) memcpy(ZEV_NAME(rec), name, name_len + 1); zev_create_checksum(rec, zp); zev_queue_message(op, msg); } void zev_znode_remove_cb(znode_t *dzp, znode_t *zp, dmu_tx_t *tx, char *name, uint64_t txtype) { int op = ZEV_OP_ZNODE_REMOVE; zev_znode_remove_t *rec; zev_msg_t *msg = NULL; int msg_size; int name_len; if (zev_skip_pool(dzp->z_zfsvfs->z_os)) return; if (zev_skip_fs(dzp->z_zfsvfs)) return; int type = (int)txtype; switch(type) { case TX_REMOVE: op = ZEV_OP_ZNODE_REMOVE; break; case TX_RMDIR: op = ZEV_OP_ZNODE_RMDIR; break; default: zev_queue_error(ZEV_OP_ZNODE_REMOVE, "ERROR: ZNODE_REMOVE: unknown txtype %d " "(dir_inode=%d:%d name='%s')\n", type, dzp->z_gen, dzp->z_id, name); return; } /* both types use the same struct, so this works for all types: */ name_len = strlen(name); msg_size = sizeof(*rec) + name_len + 1; msg = zev_alloc(sizeof(*msg) + msg_size); msg->size = msg_size; rec = (zev_znode_remove_t *)(msg + 1); rec->record_len = msg_size; rec->op = op; rec->op_time = ddi_get_time(); rec->guid = dsl_dataset_phys(dzp->z_zfsvfs->z_os->os_dsl_dataset)->ds_guid; rec->txg = dmu_tx_get_txg(tx); ZEV_FILL_INODE_INFO(file, zp); ZEV_FILL_INODE_INFO(parent, dzp); rec->name_len = name_len; (void) memcpy(ZEV_NAME(rec), name, name_len + 1); zev_queue_message(op, msg); } void zev_znode_link_cb(znode_t *dzp, znode_t *zp, dmu_tx_t *tx, char *name) { int op = ZEV_OP_ZNODE_LINK; zev_znode_link_t *rec; zev_msg_t *msg = NULL; int msg_size; int name_len; if (zev_skip_pool(zp->z_zfsvfs->z_os)) return; if (zev_skip_fs(zp->z_zfsvfs)) return; name_len = strlen(name); msg_size = sizeof(*rec) + name_len + 1; msg = zev_alloc(sizeof(*msg) + msg_size); msg->size = msg_size; rec = (zev_znode_link_t *)(msg + 1); rec->record_len = msg_size; rec->op = op; rec->op_time = ddi_get_time(); rec->guid = dsl_dataset_phys(zp->z_zfsvfs->z_os->os_dsl_dataset)->ds_guid; rec->txg = dmu_tx_get_txg(tx); ZEV_FILL_INODE_INFO(parent, dzp); ZEV_FILL_INODE_INFO(file, zp); rec->name_len = name_len; (void) memcpy(ZEV_NAME(rec), name, name_len + 1); zev_queue_message(op, msg); } void zev_znode_symlink_cb(znode_t *dzp, znode_t *zp, dmu_tx_t *tx, char *name, char *link) { int op = ZEV_OP_ZNODE_SYMLINK; zev_znode_symlink_t *rec; zev_msg_t *msg = NULL; int msg_size; int name_len; int link_len; if (zev_skip_pool(zp->z_zfsvfs->z_os)) return; if (zev_skip_fs(zp->z_zfsvfs)) return; name_len = strlen(name); link_len = strlen(link); msg_size = sizeof(*rec) + name_len + 1 + link_len + 1; msg = zev_alloc(sizeof(*msg) + msg_size); msg->size = msg_size; rec = (zev_znode_symlink_t *)(msg + 1); rec->record_len = msg_size; rec->op = op; rec->op_time = ddi_get_time(); rec->guid = dsl_dataset_phys(dzp->z_zfsvfs->z_os->os_dsl_dataset)->ds_guid; rec->txg = dmu_tx_get_txg(tx); ZEV_FILL_INODE_INFO(parent, dzp); ZEV_FILL_INODE_INFO(file, zp); rec->name_len = name_len; rec->link_len = link_len; (void) memcpy(ZEV_NAME(rec), name, name_len + 1); (void) memcpy(ZEV_LINK(rec), link, link_len + 1); zev_symlink_checksum(rec, link); zev_queue_message(op, msg); } void zev_znode_rename_cb(znode_t *sdzp, char *sname, znode_t *tdzp, char *tname, znode_t *szp, znode_t *tzp, dmu_tx_t *tx) { int op = ZEV_OP_ZNODE_RENAME; zev_znode_rename_t *rec; zev_msg_t *msg = NULL; int msg_size; int srcname_len; int dstname_len; if (zev_skip_pool(szp->z_zfsvfs->z_os)) return; if (zev_skip_fs(szp->z_zfsvfs)) return; srcname_len = strlen(sname); dstname_len = strlen(tname); msg_size = sizeof(*rec) + srcname_len + 1 + dstname_len + 1; msg = zev_alloc(sizeof(*msg) + msg_size); msg->size = msg_size; rec = (zev_znode_rename_t *)(msg + 1); rec->record_len = msg_size; rec->op = op; rec->op_time = ddi_get_time(); rec->guid = dsl_dataset_phys(szp->z_zfsvfs->z_os->os_dsl_dataset)->ds_guid; rec->txg = dmu_tx_get_txg(tx); ZEV_FILL_INODE_INFO(srcdir, sdzp); ZEV_FILL_INODE_INFO(dstdir, tdzp); ZEV_FILL_INODE_INFO(file, szp); if (tzp) { ZEV_FILL_INODE_INFO(clobbered_file, tzp); } else { memset(&rec->clobbered_file, 0, sizeof(rec->clobbered_file)); } rec->srcname_len = srcname_len; rec->dstname_len = dstname_len; (void) memcpy(ZEV_SRCNAME(rec), sname, srcname_len + 1); (void) memcpy(ZEV_DSTNAME(rec), tname, dstname_len + 1); zev_queue_message(op, msg); } void zev_znode_write_cb(znode_t *zp, dmu_tx_t *tx, uint64_t off, uint64_t len) { int op = ZEV_OP_ZNODE_WRITE; zev_znode_write_t *rec; zev_msg_t *msg = NULL; int msg_size; zev_sig_t *sig_buf; uint64_t sig_buf_len; uint64_t sig_len; uint64_t sig_cnt; int ret; if (zev_skip_pool(zp->z_zfsvfs->z_os)) return; if (zev_skip_fs(zp->z_zfsvfs)) return; ret = zev_get_checksums(&sig_buf, &sig_buf_len, &sig_cnt, 0, zp, off, len, zev_write); if (ret) { zev_queue_error(op, "ERROR: ZNODE_WRITE: can't get checksum (inode=%d:%d)\n", zp->z_gen, zp->z_id); return; } sig_len = sig_cnt * sizeof(zev_sig_t); msg_size = sizeof(*rec) + sig_len; msg = zev_alloc(sizeof(*msg) + msg_size); msg->size = msg_size; rec = (zev_znode_write_t *)(msg + 1); rec->record_len = msg_size; rec->op = op; rec->op_time = ddi_get_time(); rec->guid = dsl_dataset_phys(zp->z_zfsvfs->z_os->os_dsl_dataset)->ds_guid; rec->txg = dmu_tx_get_txg(tx); ZEV_FILL_INODE_INFO(file, zp); rec->offset = off; rec->length = len; rec->signature_cnt = sig_cnt; if (sig_cnt && sig_buf) memcpy(ZEV_SIGNATURES(rec), sig_buf, sig_len); if (sig_buf) zev_free(sig_buf, sig_buf_len); zev_queue_message(op, msg); } void zev_znode_truncate_cb(znode_t *zp, dmu_tx_t *tx, uint64_t off, uint64_t len) { int op = ZEV_OP_ZNODE_TRUNCATE; zev_znode_truncate_t *rec; zev_msg_t *msg = NULL; int msg_size; zev_sig_t *sig_buf; uint64_t sig_buf_len; uint64_t sig_len; uint64_t sig_cnt; int ret; if (zev_skip_pool(zp->z_zfsvfs->z_os)) return; if (zev_skip_fs(zp->z_zfsvfs)) return; ret = zev_get_checksums(&sig_buf, &sig_buf_len, &sig_cnt, 0, zp, off, len, zev_truncate); if (ret) { zev_queue_error(op, "ERROR: ZNODE_TRUNCATE: can't get checksum (inode=%d:%d)\n", zp->z_gen, zp->z_id); return; } sig_len = sig_cnt * sizeof(zev_sig_t); msg_size = sizeof(*rec) + sig_len; msg = zev_alloc(sizeof(*msg) + msg_size); msg->size = msg_size; rec = (zev_znode_truncate_t *)(msg + 1); rec->record_len = msg_size; rec->op = op; rec->op_time = ddi_get_time(); rec->guid = dsl_dataset_phys(zp->z_zfsvfs->z_os->os_dsl_dataset)->ds_guid; rec->txg = dmu_tx_get_txg(tx); ZEV_FILL_INODE_INFO(file, zp); rec->offset = off; rec->length = len; rec->signature_cnt = sig_cnt; if (sig_cnt && sig_buf) memcpy(ZEV_SIGNATURES(rec), sig_buf, sig_len); if (sig_buf) zev_free(sig_buf, sig_buf_len); zev_queue_message(op, msg); } void zev_znode_setattr_cb(znode_t *zp, dmu_tx_t *tx) { int op = ZEV_OP_ZNODE_SETATTR; zev_znode_setattr_t *rec; zev_msg_t *msg = NULL; int msg_size; if (zev_skip_pool(zp->z_zfsvfs->z_os)) return; if (zev_skip_fs(zp->z_zfsvfs)) return; msg_size = sizeof(*rec); msg = zev_alloc(sizeof(*msg) + msg_size); msg->size = msg_size; rec = (zev_znode_setattr_t *)(msg + 1); rec->record_len = msg_size; rec->op = op; rec->op_time = ddi_get_time(); rec->guid = dsl_dataset_phys(zp->z_zfsvfs->z_os->os_dsl_dataset)->ds_guid; rec->txg = dmu_tx_get_txg(tx); ZEV_FILL_INODE_INFO(file, zp); zev_queue_message(op, msg); } void zev_znode_acl_cb(znode_t *zp, dmu_tx_t *tx) { int op = ZEV_OP_ZNODE_ACL; zev_znode_acl_t *rec; zev_msg_t *msg = NULL; int msg_size; if (zev_skip_pool(zp->z_zfsvfs->z_os)) return; if (zev_skip_fs(zp->z_zfsvfs)) return; msg_size = sizeof(*rec); msg = zev_alloc(sizeof(*msg) + msg_size); msg->size = msg_size; rec = (zev_znode_acl_t *)(msg + 1); rec->record_len = msg_size; rec->op = op; rec->op_time = ddi_get_time(); rec->guid = dsl_dataset_phys(zp->z_zfsvfs->z_os->os_dsl_dataset)->ds_guid; rec->txg = dmu_tx_get_txg(tx); ZEV_FILL_INODE_INFO(file, zp); zev_queue_message(op, msg); } rz_zev_callbacks_t zev_callbacks = { /* zfsvfs events */ .rz_zev_zfs_mount = zev_zfs_mount_cb, .rz_zev_zfs_umount = zev_zfs_umount_cb, /* zvol zil events */ .rz_zev_zvol_truncate = zev_zvol_truncate_cb, .rz_zev_zvol_write = zev_zvol_write_cb, /* znode zil events */ .rz_zev_znode_close_after_update = zev_znode_close_after_update_cb, .rz_zev_znode_create = zev_znode_create_cb, .rz_zev_znode_remove = zev_znode_remove_cb, .rz_zev_znode_link = zev_znode_link_cb, .rz_zev_znode_symlink = zev_znode_symlink_cb, .rz_zev_znode_rename = zev_znode_rename_cb, .rz_zev_znode_write = zev_znode_write_cb, .rz_zev_znode_truncate = zev_znode_truncate_cb, .rz_zev_znode_setattr = zev_znode_setattr_cb, .rz_zev_znode_acl = zev_znode_acl_cb, };