xref: /titanic_44/usr/src/uts/common/fs/getgen/getgen.c (revision 12ae924a5d4f6af89ee47f2908555a487618d82b)
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