xref: /titanic_41/usr/src/uts/common/fs/getgen/getgen.c (revision 4b0d01e9d944e10498c80bc88d80a2f5cdd9be22)
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;
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 
49 	if (ddi_copyin((void *)arg, &gg, sizeof(gg), mode) != 0)
50 		return EFAULT;
51 	fp = getf(gg.fd);
52 	if (fp == NULL)
53 		return EBADF;
54 	if (fp->f_vnode->v_vfsp->vfs_fstype != zfsfstype) {
55 		ret = EINVAL;
56 		goto out;
57 	}
58 
59 	/* get dataset name */
60 	zfsvfs = ((znode_t *)fp->f_vnode->v_data)->z_zfsvfs;
61 	dsl_dataset_name(zfsvfs->z_os->os_dsl_dataset, gg.dataset);
62 
63 	/* get guid */
64 	gg.guid = zfsvfs->z_os->os_dsl_dataset->ds_phys->ds_guid;
65 
66 	/* get generation and crtime */
67 	osp = zfsvfs->z_os;
68 	ret = sa_setup(osp, gg.inode, zfs_attr_table, ZPL_END, &sa_table);
69 	if (ret)
70 		goto out;
71 	ret = sa_buf_hold(osp, gg.inode, FTAG, &db);
72 	if (ret)
73 		goto out;
74 	dmu_object_info_from_db(db, &doi);
75 	if ((doi.doi_bonus_type != DMU_OT_SA &&
76 	    doi.doi_bonus_type != DMU_OT_ZNODE) ||
77 	    doi.doi_bonus_type == DMU_OT_ZNODE &&
78 	    doi.doi_bonus_size < sizeof (znode_phys_t)) {
79 		sa_buf_rele(db, FTAG);
80 		ret = ENOTSUP;
81 		goto out;
82 	}
83 	ret = sa_handle_get(osp, gg.inode, NULL, SA_HDL_PRIVATE, &hdl);
84 	if (ret) {
85 		sa_buf_rele(db, FTAG);
86 		goto out;
87 	}
88 	SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_GEN], NULL,
89 		&gen, sizeof(gen));
90 	SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_CRTIME], NULL,
91 		&crtime, sizeof(crtime));
92 	ret = sa_bulk_lookup(hdl, bulk, count);
93 	sa_handle_destroy(hdl);
94 	sa_buf_rele(db, FTAG);
95 	if (ret)
96 		goto out;
97 	ZFS_TIME_DECODE(&crtime_s, crtime);
98 	gg.generation = gen;
99 	gg.crtime = crtime_s.tv_sec;
100 
101 	ddi_copyout(&gg, (void *)arg, sizeof(gg), mode);
102 out:
103 	releasef(gg.fd);
104 	return ret;
105 }
106 
107 /* ARGSUSED */
108 static int
109 gg_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
110 {
111 	int instance;
112 
113 	instance = getminor(dev);
114 	if (ddi_get_soft_state(statep, instance) == NULL)
115 		return (ENXIO);
116 	/*
117 	 * all structures passed between kernel and userspace
118 	 * are compatible between 64 and 32 bit.  Model
119 	 * conversion can be ignored.
120 	 */
121 	switch (cmd) {
122 	case GG_IOC_GET_GENERATION:
123 		return gg_ioc_get_gen(arg, mode);
124 	default:
125 		/* generic "ioctl unknown" error */
126 		return ENOTTY;
127 	}
128 	return (0);
129 }
130 
131 /* gg_close() is called when the final fd/reference is removed. */
132 /* ARGSUSED */
133 static int
134 gg_close(dev_t dev, int flag, int otyp, cred_t *crepd)
135 {
136 	gg_state_t *sp;
137 	int instance;
138 
139 	instance = getminor(dev);
140 	if ((sp = ddi_get_soft_state(statep, instance)) == NULL)
141 		return (ENXIO);
142 	if (otyp != OTYP_CHR)
143 		return (EINVAL);
144 	mutex_enter(&sp->mutex);
145 	if (sp->busy != B_TRUE) {
146 		mutex_exit(&sp->mutex);
147 		return (EINVAL);
148 	}
149 	sp->busy = B_FALSE;
150 	mutex_exit(&sp->mutex);
151 	return (0);
152 }
153 
154 /* gg_open() is called every time the device is opened/mounted/duped. */
155 /* ARGSUSED */
156 static int
157 gg_open(dev_t *devp, int flag, int otyp, cred_t *credp)
158 {
159 	gg_state_t *sp;
160 	int instance;
161 
162 	instance = getminor(*devp);
163 	if ((sp = ddi_get_soft_state(statep, instance)) == NULL)
164 		return (ENXIO);
165 	if (otyp != OTYP_CHR)
166 		return (EINVAL);
167 	if (drv_priv(credp) != 0)
168 		return (EPERM);
169 	mutex_enter(&sp->mutex);
170 	if ((flag & FEXCL) && (sp->busy == B_TRUE)) {
171 		mutex_exit(&sp->mutex);
172 		return (EBUSY);
173 	}
174 	sp->busy = B_TRUE;
175 	mutex_exit(&sp->mutex);
176 	return (0);
177 }
178 
179 static struct cb_ops gg_cb_ops = {
180 	gg_open,		/* open */
181 	gg_close,		/* close */
182 	nodev,			/* strategy */
183 	nodev,			/* print */
184 	nodev,			/* dump */
185 	nodev,			/* read */
186 	nodev,			/* write */
187 	gg_ioctl,		/* ioctl */
188 	nodev,			/* devmap */
189 	nodev,			/* mmap */
190 	nodev,			/* segmap */
191 	nochpoll,		/* chpoll */
192 	ddi_prop_op,		/* prop_op */
193 	NULL,			/* streamtab */
194 	D_MP | D_64BIT,		/* cb_flag */
195 	CB_REV,			/* cb_rev */
196 	nodev,			/* aread */
197 	nodev,			/* awrite */
198 };
199 
200 static int
201 gg_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
202 {
203 	int instance;
204 	gg_state_t *sp;
205 	/* called once per instance with DDI_DETACH,
206 	   may be called to suspend */
207 	switch (cmd) {
208 	case DDI_DETACH:
209 		/* instance busy? */
210 		instance = ddi_get_instance(dip);
211 		if ((sp = ddi_get_soft_state(statep, instance)) == NULL)
212 			return (DDI_FAILURE);
213 		mutex_enter(&sp->mutex);
214 		if (sp->busy == B_TRUE) {
215 			mutex_exit(&sp->mutex);
216 			return (DDI_FAILURE);
217 		}
218 		mutex_exit(&sp->mutex);
219 		/* free resources allocated for this instance */
220 		mutex_destroy(&sp->mutex);
221 		ddi_remove_minor_node(dip, NULL);
222 		ddi_soft_state_free(statep, instance);
223 		mutex_enter(&gg_mutex);
224 		gg_instances--;
225 		mutex_exit(&gg_mutex);
226 		return (DDI_SUCCESS);
227 	case DDI_SUSPEND:
228 		return (DDI_FAILURE);
229 	default:
230 		return (DDI_FAILURE);
231 	}
232 }
233 
234 static int
235 gg_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
236 {
237 	/* called once per instance with DDI_ATTACH,
238 	   may be called to resume */
239 	int instance;
240 	gg_state_t *sp;
241 	switch (cmd) {
242 	case DDI_ATTACH:
243 		instance = ddi_get_instance(dip);
244 		if (ddi_soft_state_zalloc(statep, instance) != DDI_SUCCESS) {
245 			return (DDI_FAILURE);
246 		}
247 		sp = ddi_get_soft_state(statep, instance);
248 		ddi_set_driver_private(dip, sp);
249 		sp->dip = dip;
250 		sp->busy = B_FALSE;
251 		if (ddi_create_minor_node(dip, ddi_get_name(dip),
252 		    S_IFCHR, instance, DDI_PSEUDO, 0) == DDI_FAILURE) {
253 			ddi_soft_state_free(statep, instance);
254 			return (DDI_FAILURE);
255 		}
256 		mutex_init(&sp->mutex, NULL, MUTEX_DRIVER, NULL);
257 		ddi_report_dev(dip);
258 		mutex_enter(&gg_mutex);
259 		gg_instances++;
260 		mutex_exit(&gg_mutex);
261 		return (DDI_SUCCESS);
262 	case DDI_RESUME:
263 		return (DDI_SUCCESS);
264 	default:
265 		return (DDI_FAILURE);
266 	}
267 }
268 
269 /* ARGSUSED */
270 static int
271 gg_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **resultp)
272 {
273 	int instance;
274 	gg_state_t *sp;
275 	switch (infocmd) {
276 	case DDI_INFO_DEVT2DEVINFO:
277 		/* arg is dev_t */
278 		instance = getminor((dev_t)arg);
279 		if ((sp = ddi_get_soft_state(statep, instance)) != NULL) {
280 			mutex_enter(&sp->mutex);
281 			*resultp = sp->dip;
282 			mutex_exit(&sp->mutex);
283 			return (DDI_SUCCESS);
284 		}
285 		*resultp = NULL;
286 		return (DDI_FAILURE);
287 	case DDI_INFO_DEVT2INSTANCE:
288 		/* arg is dev_t */
289 		instance = getminor((dev_t)arg);
290 		*resultp = (void *)(uintptr_t)instance;
291 		return (DDI_FAILURE);
292 	}
293 	return (DDI_FAILURE);
294 }
295 
296 static struct dev_ops gg_dev_ops = {
297 	DEVO_REV,			/* driver build revision */
298 	0,				/* driver reference count */
299 	gg_getinfo,			/* getinfo */
300 	nulldev,			/* identify (obsolete) */
301 	nulldev,			/* probe (search for devices) */
302 	gg_attach,			/* attach */
303 	gg_detach,			/* detach */
304 	nodev,				/* reset (obsolete, use quiesce) */
305 	&gg_cb_ops,			/* character and block device ops */
306 	NULL,				/* bus driver ops */
307 	NULL,				/* power management, not needed */
308 	ddi_quiesce_not_needed,		/* quiesce */
309 };
310 
311 static struct modldrv gg_modldrv = {
312 	&mod_driverops,			/* all loadable modules use this */
313 	"getgen - znode info, v1.0",	/* driver name and version info */
314 	&gg_dev_ops			/* ops method pointers */
315 };
316 
317 static struct modlinkage gg_modlinkage = {
318 	MODREV_1,	/* fixed value */
319 	{
320 		&gg_modldrv,	/* driver linkage structure */
321 		NULL		/* list terminator */
322 	}
323 };
324 
325 int
326 _init(void)
327 {
328 	int error;
329 
330 	if ((error = ddi_soft_state_init(&statep, sizeof(gg_state_t), 1)) != 0)
331 		return (error);
332 	gg_instances = 0;
333 
334 	mutex_init(&gg_mutex, NULL, MUTEX_DRIVER, NULL);
335 
336 	if ((error = mod_install(&gg_modlinkage)) != 0) {
337 		cmn_err(CE_WARN, "getgen: could not install module");
338 		mutex_destroy(&gg_mutex);
339 		ddi_soft_state_fini(&statep);
340 		return (error);
341 	}
342 	return (0);
343 }
344 
345 int
346 _info(struct modinfo *modinfop)
347 {
348 	return (mod_info(&gg_modlinkage, modinfop));
349 }
350 
351 int
352 _fini(void)
353 {
354 	int error = 0;
355 
356 	mutex_enter(&gg_mutex);
357 	if (gg_instances > 0) {
358 		mutex_exit(&gg_mutex);
359 		return (SET_ERROR(EBUSY));
360 	}
361 	mutex_exit(&gg_mutex);
362 
363 	if ((error = mod_remove(&gg_modlinkage)) != 0) {
364 		cmn_err(CE_WARN, "mod_remove failed: %d", error);
365 		return (error);
366 	}
367 
368 	/* free resources */
369 	ddi_soft_state_fini(&statep);
370 	mutex_destroy(&gg_mutex);
371 
372 	return (0);
373 }
374 
375