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 /* ARGSUSED */
132 static int
gg_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)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
gg_close(dev_t dev,int flag,int otyp,cred_t * crepd)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
gg_open(dev_t * devp,int flag,int otyp,cred_t * credp)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
gg_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
gg_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
gg_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** resultp)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
_init(void)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
_info(struct modinfo * modinfop)371 _info(struct modinfo *modinfop)
372 {
373 return (mod_info(&gg_modlinkage, modinfop));
374 }
375
376 int
_fini(void)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