1 #include <sys/modctl.h> 2 #include <sys/ddi.h> 3 #include <sys/sunddi.h> 4 #include <sys/conf.h> 5 #include <sys/devops.h> 6 #include <sys/stat.h> 7 #include <sys/zfs_znode.h> 8 #include <sys/time.h> 9 #include <sys/sa.h> 10 #include <sys/zap.h> 11 #include <sys/time.h> 12 #include <sys/dsl_dataset.h> 13 #include <sys/zfs_vfsops.h> 14 #include <sys/dmu.h> 15 #include <sys/dmu_objset.h> 16 #include <sys/dsl_dir.h> 17 18 #include <getgen/getgen.h> 19 20 /* per-instance context */ 21 typedef struct gg_state { 22 kmutex_t mutex; 23 dev_info_t *dip; 24 boolean_t busy; 25 } gg_state_t; 26 27 static void *statep; 28 static kmutex_t gg_mutex; 29 static int gg_instances = 0; 30 31 int 32 gg_ioc_get_gen(intptr_t arg, int mode) 33 { 34 gg_ioctl_get_generation_t gg; 35 file_t *fp; 36 uint64_t gen; 37 uint64_t crtime[2]; 38 int ret = 0; 39 zfsvfs_t *zfsvfs = NULL; 40 objset_t *osp; 41 sa_attr_type_t *sa_table; 42 sa_handle_t *hdl; 43 dmu_buf_t *db; 44 sa_bulk_attr_t bulk[2]; 45 int count = 0; 46 dmu_object_info_t doi; 47 timestruc_t crtime_s; 48 znode_t *zp; 49 dsl_dataset_phys_t *ds_phys; 50 51 if (ddi_copyin((void *)arg, &gg, sizeof(gg), mode) != 0) 52 return EFAULT; 53 fp = getf(gg.fd); 54 if (fp == NULL) 55 return EBADF; 56 if (fp->f_vnode->v_vfsp->vfs_fstype != zfsfstype) { 57 ret = EINVAL; 58 goto out; 59 } 60 61 zp = (znode_t *)fp->f_vnode->v_data; 62 /* modified version of ZFS_ENTER() macro - we need to clean up fp */ 63 zfsvfs = zp->z_zfsvfs; 64 rrm_enter_read(&zfsvfs->z_teardown_lock, FTAG); 65 if (zp->z_zfsvfs->z_unmounted) { 66 ret = EIO; 67 goto out; 68 } 69 /* modified version of ZFS_VERIFY_ZP() macro */ 70 if (zp->z_sa_hdl == NULL) { 71 ret = EIO; 72 goto out; 73 } 74 ds_phys = dsl_dataset_phys(zfsvfs->z_os->os_dsl_dataset); 75 76 /* get dataset name */ 77 dsl_dataset_name(zfsvfs->z_os->os_dsl_dataset, gg.dataset); 78 79 /* get guid */ 80 gg.guid = ds_phys->ds_guid; 81 82 if (gg.inode != 0) { 83 /* get generation and crtime */ 84 osp = zfsvfs->z_os; 85 ret = sa_setup(osp, gg.inode, zfs_attr_table, ZPL_END, 86 &sa_table); 87 if (ret) 88 goto out; 89 ret = sa_buf_hold(osp, gg.inode, FTAG, &db); 90 if (ret) 91 goto out; 92 dmu_object_info_from_db(db, &doi); 93 if ((doi.doi_bonus_type != DMU_OT_SA && 94 doi.doi_bonus_type != DMU_OT_ZNODE) || 95 doi.doi_bonus_type == DMU_OT_ZNODE && 96 doi.doi_bonus_size < sizeof (znode_phys_t)) { 97 sa_buf_rele(db, FTAG); 98 ret = ENOTSUP; 99 goto out; 100 } 101 ret = sa_handle_get(osp, gg.inode, NULL, SA_HDL_PRIVATE, &hdl); 102 if (ret) { 103 sa_buf_rele(db, FTAG); 104 goto out; 105 } 106 SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_GEN], NULL, 107 &gen, sizeof(gen)); 108 SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_CRTIME], NULL, 109 &crtime, sizeof(crtime)); 110 ret = sa_bulk_lookup(hdl, bulk, count); 111 sa_handle_destroy(hdl); 112 sa_buf_rele(db, FTAG); 113 if (ret) 114 goto out; 115 ZFS_TIME_DECODE(&crtime_s, crtime); 116 gg.generation = gen; 117 gg.crtime = crtime_s.tv_sec; 118 } else { 119 gg.generation = ds_phys->ds_bp.blk_birth; 120 gg.crtime = 0; 121 } 122 123 ddi_copyout(&gg, (void *)arg, sizeof(gg), mode); 124 out: 125 if (zfsvfs) 126 ZFS_EXIT(zfsvfs); 127 releasef(gg.fd); 128 return ret; 129 } 130 131 int 132 gg_ioc_get_txg(intptr_t arg, int mode) 133 { 134 gg_ioctl_get_txg_t gg; 135 dsl_pool_t *dp; 136 dsl_dataset_t *ds; 137 dsl_dataset_phys_t *ds_phys; 138 int error; 139 140 if (ddi_copyin((void *)arg, &gg, sizeof(gg), mode) != 0) 141 return EFAULT; 142 143 error = dsl_pool_hold(gg.dataset, FTAG, &dp); 144 if (error != 0) 145 return (error); 146 147 error = dsl_dataset_hold(dp, gg.dataset, FTAG, &ds); 148 if (error == 0) { 149 ds_phys = dsl_dataset_phys(ds); 150 gg.txg = ds_phys->ds_bp.blk_birth; 151 dsl_dataset_rele(ds, &gg); 152 } 153 dsl_pool_rele(dp, FTAG); 154 155 ddi_copyout(&gg, (void *)arg, sizeof(gg), mode); 156 157 return (error); 158 } 159 160 /* ARGSUSED */ 161 static int 162 gg_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp) 163 { 164 int instance; 165 166 instance = getminor(dev); 167 if (ddi_get_soft_state(statep, instance) == NULL) 168 return (ENXIO); 169 /* 170 * all structures passed between kernel and userspace 171 * are compatible between 64 and 32 bit. Model 172 * conversion can be ignored. 173 */ 174 switch (cmd) { 175 case GG_IOC_GET_GENERATION: 176 return gg_ioc_get_gen(arg, mode); 177 case GG_IOC_GET_TXG: 178 return gg_ioc_get_txg(arg, mode); 179 default: 180 /* generic "ioctl unknown" error */ 181 return ENOTTY; 182 } 183 /* NOTREACHED */ 184 return (0); 185 } 186 187 /* gg_close() is called when the final fd/reference is removed. */ 188 /* ARGSUSED */ 189 static int 190 gg_close(dev_t dev, int flag, int otyp, cred_t *crepd) 191 { 192 gg_state_t *sp; 193 int instance; 194 195 instance = getminor(dev); 196 if ((sp = ddi_get_soft_state(statep, instance)) == NULL) 197 return (ENXIO); 198 if (otyp != OTYP_CHR) 199 return (EINVAL); 200 mutex_enter(&sp->mutex); 201 if (sp->busy != B_TRUE) { 202 mutex_exit(&sp->mutex); 203 return (EINVAL); 204 } 205 sp->busy = B_FALSE; 206 mutex_exit(&sp->mutex); 207 return (0); 208 } 209 210 /* gg_open() is called every time the device is opened/mounted/duped. */ 211 /* ARGSUSED */ 212 static int 213 gg_open(dev_t *devp, int flag, int otyp, cred_t *credp) 214 { 215 gg_state_t *sp; 216 int instance; 217 218 instance = getminor(*devp); 219 if ((sp = ddi_get_soft_state(statep, instance)) == NULL) 220 return (ENXIO); 221 if (otyp != OTYP_CHR) 222 return (EINVAL); 223 if (drv_priv(credp) != 0) 224 return (EPERM); 225 mutex_enter(&sp->mutex); 226 if ((flag & FEXCL) && (sp->busy == B_TRUE)) { 227 mutex_exit(&sp->mutex); 228 return (EBUSY); 229 } 230 sp->busy = B_TRUE; 231 mutex_exit(&sp->mutex); 232 return (0); 233 } 234 235 static struct cb_ops gg_cb_ops = { 236 gg_open, /* open */ 237 gg_close, /* close */ 238 nodev, /* strategy */ 239 nodev, /* print */ 240 nodev, /* dump */ 241 nodev, /* read */ 242 nodev, /* write */ 243 gg_ioctl, /* ioctl */ 244 nodev, /* devmap */ 245 nodev, /* mmap */ 246 nodev, /* segmap */ 247 nochpoll, /* chpoll */ 248 ddi_prop_op, /* prop_op */ 249 NULL, /* streamtab */ 250 D_MP | D_64BIT, /* cb_flag */ 251 CB_REV, /* cb_rev */ 252 nodev, /* aread */ 253 nodev, /* awrite */ 254 }; 255 256 static int 257 gg_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 258 { 259 int instance; 260 gg_state_t *sp; 261 /* called once per instance with DDI_DETACH, 262 may be called to suspend */ 263 switch (cmd) { 264 case DDI_DETACH: 265 /* instance busy? */ 266 instance = ddi_get_instance(dip); 267 if ((sp = ddi_get_soft_state(statep, instance)) == NULL) 268 return (DDI_FAILURE); 269 mutex_enter(&sp->mutex); 270 if (sp->busy == B_TRUE) { 271 mutex_exit(&sp->mutex); 272 return (DDI_FAILURE); 273 } 274 mutex_exit(&sp->mutex); 275 /* free resources allocated for this instance */ 276 mutex_destroy(&sp->mutex); 277 ddi_remove_minor_node(dip, NULL); 278 ddi_soft_state_free(statep, instance); 279 mutex_enter(&gg_mutex); 280 gg_instances--; 281 mutex_exit(&gg_mutex); 282 return (DDI_SUCCESS); 283 case DDI_SUSPEND: 284 return (DDI_FAILURE); 285 default: 286 return (DDI_FAILURE); 287 } 288 } 289 290 static int 291 gg_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 292 { 293 /* called once per instance with DDI_ATTACH, 294 may be called to resume */ 295 int instance; 296 gg_state_t *sp; 297 switch (cmd) { 298 case DDI_ATTACH: 299 instance = ddi_get_instance(dip); 300 if (ddi_soft_state_zalloc(statep, instance) != DDI_SUCCESS) { 301 return (DDI_FAILURE); 302 } 303 sp = ddi_get_soft_state(statep, instance); 304 ddi_set_driver_private(dip, sp); 305 sp->dip = dip; 306 sp->busy = B_FALSE; 307 if (ddi_create_minor_node(dip, ddi_get_name(dip), 308 S_IFCHR, instance, DDI_PSEUDO, 0) == DDI_FAILURE) { 309 ddi_soft_state_free(statep, instance); 310 return (DDI_FAILURE); 311 } 312 mutex_init(&sp->mutex, NULL, MUTEX_DRIVER, NULL); 313 ddi_report_dev(dip); 314 mutex_enter(&gg_mutex); 315 gg_instances++; 316 mutex_exit(&gg_mutex); 317 return (DDI_SUCCESS); 318 case DDI_RESUME: 319 return (DDI_SUCCESS); 320 default: 321 return (DDI_FAILURE); 322 } 323 } 324 325 /* ARGSUSED */ 326 static int 327 gg_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **resultp) 328 { 329 int instance; 330 gg_state_t *sp; 331 switch (infocmd) { 332 case DDI_INFO_DEVT2DEVINFO: 333 /* arg is dev_t */ 334 instance = getminor((dev_t)arg); 335 if ((sp = ddi_get_soft_state(statep, instance)) != NULL) { 336 mutex_enter(&sp->mutex); 337 *resultp = sp->dip; 338 mutex_exit(&sp->mutex); 339 return (DDI_SUCCESS); 340 } 341 *resultp = NULL; 342 return (DDI_FAILURE); 343 case DDI_INFO_DEVT2INSTANCE: 344 /* arg is dev_t */ 345 instance = getminor((dev_t)arg); 346 *resultp = (void *)(uintptr_t)instance; 347 return (DDI_FAILURE); 348 } 349 return (DDI_FAILURE); 350 } 351 352 static struct dev_ops gg_dev_ops = { 353 DEVO_REV, /* driver build revision */ 354 0, /* driver reference count */ 355 gg_getinfo, /* getinfo */ 356 nulldev, /* identify (obsolete) */ 357 nulldev, /* probe (search for devices) */ 358 gg_attach, /* attach */ 359 gg_detach, /* detach */ 360 nodev, /* reset (obsolete, use quiesce) */ 361 &gg_cb_ops, /* character and block device ops */ 362 NULL, /* bus driver ops */ 363 NULL, /* power management, not needed */ 364 ddi_quiesce_not_needed, /* quiesce */ 365 }; 366 367 static struct modldrv gg_modldrv = { 368 &mod_driverops, /* all loadable modules use this */ 369 "getgen - znode info, v1.1", /* driver name and version info */ 370 &gg_dev_ops /* ops method pointers */ 371 }; 372 373 static struct modlinkage gg_modlinkage = { 374 MODREV_1, /* fixed value */ 375 { 376 &gg_modldrv, /* driver linkage structure */ 377 NULL /* list terminator */ 378 } 379 }; 380 381 int 382 _init(void) 383 { 384 int error; 385 386 if ((error = ddi_soft_state_init(&statep, sizeof(gg_state_t), 1)) != 0) 387 return (error); 388 gg_instances = 0; 389 390 mutex_init(&gg_mutex, NULL, MUTEX_DRIVER, NULL); 391 392 if ((error = mod_install(&gg_modlinkage)) != 0) { 393 cmn_err(CE_WARN, "getgen: could not install module"); 394 mutex_destroy(&gg_mutex); 395 ddi_soft_state_fini(&statep); 396 return (error); 397 } 398 return (0); 399 } 400 401 int 402 _info(struct modinfo *modinfop) 403 { 404 return (mod_info(&gg_modlinkage, modinfop)); 405 } 406 407 int 408 _fini(void) 409 { 410 int error = 0; 411 412 mutex_enter(&gg_mutex); 413 if (gg_instances > 0) { 414 mutex_exit(&gg_mutex); 415 return (SET_ERROR(EBUSY)); 416 } 417 mutex_exit(&gg_mutex); 418 419 if ((error = mod_remove(&gg_modlinkage)) != 0) { 420 cmn_err(CE_WARN, "mod_remove failed: %d", error); 421 return (error); 422 } 423 424 /* free resources */ 425 ddi_soft_state_fini(&statep); 426 mutex_destroy(&gg_mutex); 427 428 return (0); 429 } 430 431