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
gg_ioc_get_gen(intptr_t arg,int mode)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
gg_ioc_get_txg(intptr_t arg,int mode)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
gg_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)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
gg_close(dev_t dev,int flag,int otyp,cred_t * crepd)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
gg_open(dev_t * devp,int flag,int otyp,cred_t * credp)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
gg_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
gg_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
gg_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** resultp)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
_init(void)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
_info(struct modinfo * modinfop)402 _info(struct modinfo *modinfop)
403 {
404 return (mod_info(&gg_modlinkage, modinfop));
405 }
406
407 int
_fini(void)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