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