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 /* ARGSUSED */ 132 static int 133 gg_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp) 134 { 135 int instance; 136 137 instance = getminor(dev); 138 if (ddi_get_soft_state(statep, instance) == NULL) 139 return (ENXIO); 140 /* 141 * all structures passed between kernel and userspace 142 * are compatible between 64 and 32 bit. Model 143 * conversion can be ignored. 144 */ 145 switch (cmd) { 146 case GG_IOC_GET_GENERATION: 147 return gg_ioc_get_gen(arg, mode); 148 default: 149 /* generic "ioctl unknown" error */ 150 return ENOTTY; 151 } 152 /* NOTREACHED */ 153 return (0); 154 } 155 156 /* gg_close() is called when the final fd/reference is removed. */ 157 /* ARGSUSED */ 158 static int 159 gg_close(dev_t dev, int flag, int otyp, cred_t *crepd) 160 { 161 gg_state_t *sp; 162 int instance; 163 164 instance = getminor(dev); 165 if ((sp = ddi_get_soft_state(statep, instance)) == NULL) 166 return (ENXIO); 167 if (otyp != OTYP_CHR) 168 return (EINVAL); 169 mutex_enter(&sp->mutex); 170 if (sp->busy != B_TRUE) { 171 mutex_exit(&sp->mutex); 172 return (EINVAL); 173 } 174 sp->busy = B_FALSE; 175 mutex_exit(&sp->mutex); 176 return (0); 177 } 178 179 /* gg_open() is called every time the device is opened/mounted/duped. */ 180 /* ARGSUSED */ 181 static int 182 gg_open(dev_t *devp, int flag, int otyp, cred_t *credp) 183 { 184 gg_state_t *sp; 185 int instance; 186 187 instance = getminor(*devp); 188 if ((sp = ddi_get_soft_state(statep, instance)) == NULL) 189 return (ENXIO); 190 if (otyp != OTYP_CHR) 191 return (EINVAL); 192 if (drv_priv(credp) != 0) 193 return (EPERM); 194 mutex_enter(&sp->mutex); 195 if ((flag & FEXCL) && (sp->busy == B_TRUE)) { 196 mutex_exit(&sp->mutex); 197 return (EBUSY); 198 } 199 sp->busy = B_TRUE; 200 mutex_exit(&sp->mutex); 201 return (0); 202 } 203 204 static struct cb_ops gg_cb_ops = { 205 gg_open, /* open */ 206 gg_close, /* close */ 207 nodev, /* strategy */ 208 nodev, /* print */ 209 nodev, /* dump */ 210 nodev, /* read */ 211 nodev, /* write */ 212 gg_ioctl, /* ioctl */ 213 nodev, /* devmap */ 214 nodev, /* mmap */ 215 nodev, /* segmap */ 216 nochpoll, /* chpoll */ 217 ddi_prop_op, /* prop_op */ 218 NULL, /* streamtab */ 219 D_MP | D_64BIT, /* cb_flag */ 220 CB_REV, /* cb_rev */ 221 nodev, /* aread */ 222 nodev, /* awrite */ 223 }; 224 225 static int 226 gg_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 227 { 228 int instance; 229 gg_state_t *sp; 230 /* called once per instance with DDI_DETACH, 231 may be called to suspend */ 232 switch (cmd) { 233 case DDI_DETACH: 234 /* instance busy? */ 235 instance = ddi_get_instance(dip); 236 if ((sp = ddi_get_soft_state(statep, instance)) == NULL) 237 return (DDI_FAILURE); 238 mutex_enter(&sp->mutex); 239 if (sp->busy == B_TRUE) { 240 mutex_exit(&sp->mutex); 241 return (DDI_FAILURE); 242 } 243 mutex_exit(&sp->mutex); 244 /* free resources allocated for this instance */ 245 mutex_destroy(&sp->mutex); 246 ddi_remove_minor_node(dip, NULL); 247 ddi_soft_state_free(statep, instance); 248 mutex_enter(&gg_mutex); 249 gg_instances--; 250 mutex_exit(&gg_mutex); 251 return (DDI_SUCCESS); 252 case DDI_SUSPEND: 253 return (DDI_FAILURE); 254 default: 255 return (DDI_FAILURE); 256 } 257 } 258 259 static int 260 gg_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 261 { 262 /* called once per instance with DDI_ATTACH, 263 may be called to resume */ 264 int instance; 265 gg_state_t *sp; 266 switch (cmd) { 267 case DDI_ATTACH: 268 instance = ddi_get_instance(dip); 269 if (ddi_soft_state_zalloc(statep, instance) != DDI_SUCCESS) { 270 return (DDI_FAILURE); 271 } 272 sp = ddi_get_soft_state(statep, instance); 273 ddi_set_driver_private(dip, sp); 274 sp->dip = dip; 275 sp->busy = B_FALSE; 276 if (ddi_create_minor_node(dip, ddi_get_name(dip), 277 S_IFCHR, instance, DDI_PSEUDO, 0) == DDI_FAILURE) { 278 ddi_soft_state_free(statep, instance); 279 return (DDI_FAILURE); 280 } 281 mutex_init(&sp->mutex, NULL, MUTEX_DRIVER, NULL); 282 ddi_report_dev(dip); 283 mutex_enter(&gg_mutex); 284 gg_instances++; 285 mutex_exit(&gg_mutex); 286 return (DDI_SUCCESS); 287 case DDI_RESUME: 288 return (DDI_SUCCESS); 289 default: 290 return (DDI_FAILURE); 291 } 292 } 293 294 /* ARGSUSED */ 295 static int 296 gg_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **resultp) 297 { 298 int instance; 299 gg_state_t *sp; 300 switch (infocmd) { 301 case DDI_INFO_DEVT2DEVINFO: 302 /* arg is dev_t */ 303 instance = getminor((dev_t)arg); 304 if ((sp = ddi_get_soft_state(statep, instance)) != NULL) { 305 mutex_enter(&sp->mutex); 306 *resultp = sp->dip; 307 mutex_exit(&sp->mutex); 308 return (DDI_SUCCESS); 309 } 310 *resultp = NULL; 311 return (DDI_FAILURE); 312 case DDI_INFO_DEVT2INSTANCE: 313 /* arg is dev_t */ 314 instance = getminor((dev_t)arg); 315 *resultp = (void *)(uintptr_t)instance; 316 return (DDI_FAILURE); 317 } 318 return (DDI_FAILURE); 319 } 320 321 static struct dev_ops gg_dev_ops = { 322 DEVO_REV, /* driver build revision */ 323 0, /* driver reference count */ 324 gg_getinfo, /* getinfo */ 325 nulldev, /* identify (obsolete) */ 326 nulldev, /* probe (search for devices) */ 327 gg_attach, /* attach */ 328 gg_detach, /* detach */ 329 nodev, /* reset (obsolete, use quiesce) */ 330 &gg_cb_ops, /* character and block device ops */ 331 NULL, /* bus driver ops */ 332 NULL, /* power management, not needed */ 333 ddi_quiesce_not_needed, /* quiesce */ 334 }; 335 336 static struct modldrv gg_modldrv = { 337 &mod_driverops, /* all loadable modules use this */ 338 "getgen - znode info, v1.1", /* driver name and version info */ 339 &gg_dev_ops /* ops method pointers */ 340 }; 341 342 static struct modlinkage gg_modlinkage = { 343 MODREV_1, /* fixed value */ 344 { 345 &gg_modldrv, /* driver linkage structure */ 346 NULL /* list terminator */ 347 } 348 }; 349 350 int 351 _init(void) 352 { 353 int error; 354 355 if ((error = ddi_soft_state_init(&statep, sizeof(gg_state_t), 1)) != 0) 356 return (error); 357 gg_instances = 0; 358 359 mutex_init(&gg_mutex, NULL, MUTEX_DRIVER, NULL); 360 361 if ((error = mod_install(&gg_modlinkage)) != 0) { 362 cmn_err(CE_WARN, "getgen: could not install module"); 363 mutex_destroy(&gg_mutex); 364 ddi_soft_state_fini(&statep); 365 return (error); 366 } 367 return (0); 368 } 369 370 int 371 _info(struct modinfo *modinfop) 372 { 373 return (mod_info(&gg_modlinkage, modinfop)); 374 } 375 376 int 377 _fini(void) 378 { 379 int error = 0; 380 381 mutex_enter(&gg_mutex); 382 if (gg_instances > 0) { 383 mutex_exit(&gg_mutex); 384 return (SET_ERROR(EBUSY)); 385 } 386 mutex_exit(&gg_mutex); 387 388 if ((error = mod_remove(&gg_modlinkage)) != 0) { 389 cmn_err(CE_WARN, "mod_remove failed: %d", error); 390 return (error); 391 } 392 393 /* free resources */ 394 ddi_soft_state_fini(&statep); 395 mutex_destroy(&gg_mutex); 396 397 return (0); 398 } 399 400