1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright (c) 2011 by Delphix. All rights reserved.
25  */
26 
27 #include <unistd.h>
28 #include <strings.h>
29 #include <stdlib.h>
30 #include <errno.h>
31 #include <assert.h>
32 #include <ctype.h>
33 #ifdef illumos
34 #include <alloca.h>
35 #endif
36 
37 #include <dt_impl.h>
38 #include <dt_program.h>
39 #include <dt_printf.h>
40 #include <dt_provider.h>
41 
42 dtrace_prog_t *
dt_program_create(dtrace_hdl_t * dtp)43 dt_program_create(dtrace_hdl_t *dtp)
44 {
45           dtrace_prog_t *pgp = dt_zalloc(dtp, sizeof (dtrace_prog_t));
46 
47           if (pgp != NULL) {
48                     dt_list_append(&dtp->dt_programs, pgp);
49           } else {
50                     (void) dt_set_errno(dtp, EDT_NOMEM);
51                     return (NULL);
52           }
53 
54           /*
55            * By default, programs start with DOF version 1 so that output files
56            * containing DOF are backward compatible. If a program requires new
57            * DOF features, the version is increased as needed.
58            */
59           pgp->dp_dofversion = DOF_VERSION_1;
60 
61           return (pgp);
62 }
63 
64 void
dt_program_destroy(dtrace_hdl_t * dtp,dtrace_prog_t * pgp)65 dt_program_destroy(dtrace_hdl_t *dtp, dtrace_prog_t *pgp)
66 {
67           dt_stmt_t *stp, *next;
68           uint_t i;
69 
70           for (stp = dt_list_next(&pgp->dp_stmts); stp != NULL; stp = next) {
71                     next = dt_list_next(stp);
72                     dtrace_stmt_destroy(dtp, stp->ds_desc);
73                     dt_free(dtp, stp);
74           }
75 
76           for (i = 0; i < pgp->dp_xrefslen; i++)
77                     dt_free(dtp, pgp->dp_xrefs[i]);
78 
79           dt_free(dtp, pgp->dp_xrefs);
80           dt_list_delete(&dtp->dt_programs, pgp);
81           dt_free(dtp, pgp);
82 }
83 
84 /*ARGSUSED*/
85 void
dtrace_program_info(dtrace_hdl_t * dtp,dtrace_prog_t * pgp,dtrace_proginfo_t * pip)86 dtrace_program_info(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
87     dtrace_proginfo_t *pip)
88 {
89           dt_stmt_t *stp;
90           dtrace_actdesc_t *ap;
91           dtrace_ecbdesc_t *last = NULL;
92 
93           if (pip == NULL)
94                     return;
95 
96           bzero(pip, sizeof (dtrace_proginfo_t));
97 
98           if (dt_list_next(&pgp->dp_stmts) != NULL) {
99                     pip->dpi_descattr = _dtrace_maxattr;
100                     pip->dpi_stmtattr = _dtrace_maxattr;
101           } else {
102                     pip->dpi_descattr = _dtrace_defattr;
103                     pip->dpi_stmtattr = _dtrace_defattr;
104           }
105 
106           for (stp = dt_list_next(&pgp->dp_stmts); stp; stp = dt_list_next(stp)) {
107                     dtrace_ecbdesc_t *edp = stp->ds_desc->dtsd_ecbdesc;
108 
109                     if (edp == last)
110                               continue;
111                     last = edp;
112 
113                     pip->dpi_descattr =
114                         dt_attr_min(stp->ds_desc->dtsd_descattr, pip->dpi_descattr);
115 
116                     pip->dpi_stmtattr =
117                         dt_attr_min(stp->ds_desc->dtsd_stmtattr, pip->dpi_stmtattr);
118 
119                     /*
120                      * If there aren't any actions, account for the fact that
121                      * recording the epid will generate a record.
122                      */
123                     if (edp->dted_action == NULL)
124                               pip->dpi_recgens++;
125 
126                     for (ap = edp->dted_action; ap != NULL; ap = ap->dtad_next) {
127                               if (ap->dtad_kind == DTRACEACT_SPECULATE) {
128                                         pip->dpi_speculations++;
129                                         continue;
130                               }
131 
132                               if (DTRACEACT_ISAGG(ap->dtad_kind)) {
133                                         pip->dpi_recgens -= ap->dtad_arg;
134                                         pip->dpi_aggregates++;
135                                         continue;
136                               }
137 
138                               if (DTRACEACT_ISDESTRUCTIVE(ap->dtad_kind))
139                                         continue;
140 
141                               if (ap->dtad_kind == DTRACEACT_DIFEXPR &&
142                                   ap->dtad_difo->dtdo_rtype.dtdt_kind ==
143                                   DIF_TYPE_CTF &&
144                                   ap->dtad_difo->dtdo_rtype.dtdt_size == 0)
145                                         continue;
146 
147                               pip->dpi_recgens++;
148                     }
149           }
150 }
151 
152 int
dtrace_program_exec(dtrace_hdl_t * dtp,dtrace_prog_t * pgp,dtrace_proginfo_t * pip)153 dtrace_program_exec(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
154     dtrace_proginfo_t *pip)
155 {
156           dtrace_enable_io_t args;
157           void *dof;
158           int n, err;
159 
160           dtrace_program_info(dtp, pgp, pip);
161 
162           if ((dof = dtrace_dof_create(dtp, pgp, DTRACE_D_STRIP)) == NULL)
163                     return (-1);
164 
165           args.dof = dof;
166           args.n_matched = 0;
167           n = dt_ioctl(dtp, DTRACEIOC_ENABLE, &args);
168           dtrace_dof_destroy(dtp, dof);
169 
170           if (n == -1) {
171                     switch (errno) {
172                     case EINVAL:
173                               err = EDT_DIFINVAL;
174                               break;
175                     case EFAULT:
176                               err = EDT_DIFFAULT;
177                               break;
178                     case E2BIG:
179                               err = EDT_DIFSIZE;
180                               break;
181                     case EBUSY:
182                               err = EDT_ENABLING_ERR;
183                               break;
184                     default:
185                               err = errno;
186                     }
187 
188                     return (dt_set_errno(dtp, err));
189           }
190 
191           if (pip != NULL)
192                     pip->dpi_matches += args.n_matched;
193 
194           return (0);
195 }
196 
197 static void
dt_ecbdesc_hold(dtrace_ecbdesc_t * edp)198 dt_ecbdesc_hold(dtrace_ecbdesc_t *edp)
199 {
200           edp->dted_refcnt++;
201 }
202 
203 void
dt_ecbdesc_release(dtrace_hdl_t * dtp,dtrace_ecbdesc_t * edp)204 dt_ecbdesc_release(dtrace_hdl_t *dtp, dtrace_ecbdesc_t *edp)
205 {
206           if (--edp->dted_refcnt > 0)
207                     return;
208 
209           dt_difo_free(dtp, edp->dted_pred.dtpdd_difo);
210           assert(edp->dted_action == NULL);
211           dt_free(dtp, edp);
212 }
213 
214 dtrace_ecbdesc_t *
dt_ecbdesc_create(dtrace_hdl_t * dtp,const dtrace_probedesc_t * pdp)215 dt_ecbdesc_create(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp)
216 {
217           dtrace_ecbdesc_t *edp;
218 
219           if ((edp = dt_zalloc(dtp, sizeof (dtrace_ecbdesc_t))) == NULL) {
220                     (void) dt_set_errno(dtp, EDT_NOMEM);
221                     return (NULL);
222           }
223 
224           edp->dted_probe = *pdp;
225           dt_ecbdesc_hold(edp);
226           return (edp);
227 }
228 
229 dtrace_stmtdesc_t *
dtrace_stmt_create(dtrace_hdl_t * dtp,dtrace_ecbdesc_t * edp)230 dtrace_stmt_create(dtrace_hdl_t *dtp, dtrace_ecbdesc_t *edp)
231 {
232           dtrace_stmtdesc_t *sdp;
233 
234           if ((sdp = dt_zalloc(dtp, sizeof (dtrace_stmtdesc_t))) == NULL)
235                     return (NULL);
236 
237           dt_ecbdesc_hold(edp);
238           sdp->dtsd_ecbdesc = edp;
239           sdp->dtsd_descattr = _dtrace_defattr;
240           sdp->dtsd_stmtattr = _dtrace_defattr;
241 
242           return (sdp);
243 }
244 
245 dtrace_actdesc_t *
dtrace_stmt_action(dtrace_hdl_t * dtp,dtrace_stmtdesc_t * sdp)246 dtrace_stmt_action(dtrace_hdl_t *dtp, dtrace_stmtdesc_t *sdp)
247 {
248           dtrace_actdesc_t *new;
249           dtrace_ecbdesc_t *edp = sdp->dtsd_ecbdesc;
250 
251           if ((new = dt_alloc(dtp, sizeof (dtrace_actdesc_t))) == NULL)
252                     return (NULL);
253 
254           if (sdp->dtsd_action_last != NULL) {
255                     assert(sdp->dtsd_action != NULL);
256                     assert(sdp->dtsd_action_last->dtad_next == NULL);
257                     sdp->dtsd_action_last->dtad_next = new;
258           } else {
259                     dtrace_actdesc_t *ap = edp->dted_action;
260 
261                     assert(sdp->dtsd_action == NULL);
262                     sdp->dtsd_action = new;
263 
264                     while (ap != NULL && ap->dtad_next != NULL)
265                               ap = ap->dtad_next;
266 
267                     if (ap == NULL)
268                               edp->dted_action = new;
269                     else
270                               ap->dtad_next = new;
271           }
272 
273           sdp->dtsd_action_last = new;
274           bzero(new, sizeof (dtrace_actdesc_t));
275           new->dtad_uarg = (uintptr_t)sdp;
276 
277           return (new);
278 }
279 
280 int
dtrace_stmt_add(dtrace_hdl_t * dtp,dtrace_prog_t * pgp,dtrace_stmtdesc_t * sdp)281 dtrace_stmt_add(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, dtrace_stmtdesc_t *sdp)
282 {
283           dt_stmt_t *stp = dt_alloc(dtp, sizeof (dt_stmt_t));
284 
285           if (stp == NULL)
286                     return (-1); /* errno is set for us */
287 
288           dt_list_append(&pgp->dp_stmts, stp);
289           stp->ds_desc = sdp;
290 
291           return (0);
292 }
293 
294 int
dtrace_stmt_iter(dtrace_hdl_t * dtp,dtrace_prog_t * pgp,dtrace_stmt_f * func,void * data)295 dtrace_stmt_iter(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
296     dtrace_stmt_f *func, void *data)
297 {
298           dt_stmt_t *stp, *next;
299           int status = 0;
300 
301           for (stp = dt_list_next(&pgp->dp_stmts); stp != NULL; stp = next) {
302                     next = dt_list_next(stp);
303                     if ((status = func(dtp, pgp, stp->ds_desc, data)) != 0)
304                               break;
305           }
306 
307           return (status);
308 }
309 
310 void
dtrace_stmt_destroy(dtrace_hdl_t * dtp,dtrace_stmtdesc_t * sdp)311 dtrace_stmt_destroy(dtrace_hdl_t *dtp, dtrace_stmtdesc_t *sdp)
312 {
313           dtrace_ecbdesc_t *edp = sdp->dtsd_ecbdesc;
314 
315           /*
316            * We need to remove any actions that we have on this ECB, and
317            * remove our hold on the ECB itself.
318            */
319           if (sdp->dtsd_action != NULL) {
320                     dtrace_actdesc_t *last = sdp->dtsd_action_last;
321                     dtrace_actdesc_t *ap, *next;
322 
323                     assert(last != NULL);
324 
325                     for (ap = edp->dted_action; ap != NULL; ap = ap->dtad_next) {
326                               if (ap == sdp->dtsd_action)
327                                         break;
328 
329                               if (ap->dtad_next == sdp->dtsd_action)
330                                         break;
331                     }
332 
333                     assert(ap != NULL);
334 
335                     if (ap == edp->dted_action)
336                               edp->dted_action = last->dtad_next;
337                     else
338                               ap->dtad_next = last->dtad_next;
339 
340                     /*
341                      * We have now removed our action list from its ECB; we can
342                      * safely destroy the list.
343                      */
344                     last->dtad_next = NULL;
345 
346                     for (ap = sdp->dtsd_action; ap != NULL; ap = next) {
347                               assert(ap->dtad_uarg == (uintptr_t)sdp);
348                               dt_difo_free(dtp, ap->dtad_difo);
349                               next = ap->dtad_next;
350                               dt_free(dtp, ap);
351                     }
352           }
353 
354           if (sdp->dtsd_fmtdata != NULL)
355                     dt_printf_destroy(sdp->dtsd_fmtdata);
356           dt_free(dtp, sdp->dtsd_strdata);
357 
358           dt_ecbdesc_release(dtp, sdp->dtsd_ecbdesc);
359           dt_free(dtp, sdp);
360 }
361 
362 typedef struct dt_header_info {
363           dtrace_hdl_t *dthi_dtp;       /* consumer handle */
364           FILE *dthi_out;               /* output file */
365           char *dthi_pmname;  /* provider macro name */
366           char *dthi_pfname;  /* provider function name */
367           int dthi_empty;               /* should we generate empty macros */
368 } dt_header_info_t;
369 
370 static void
dt_header_fmt_macro(char * buf,const char * str)371 dt_header_fmt_macro(char *buf, const char *str)
372 {
373           for (;;) {
374                     if (islower((unsigned char)*str)) {
375                               *buf++ = *str++ + 'A' - 'a';
376                     } else if (*str == '-') {
377                               *buf++ = '_';
378                               str++;
379                     } else if (*str == '.') {
380                               *buf++ = '_';
381                               str++;
382                     } else if ((*buf++ = *str++) == '\0') {
383                               break;
384                     }
385           }
386 }
387 
388 static void
dt_header_fmt_func(char * buf,const char * str)389 dt_header_fmt_func(char *buf, const char *str)
390 {
391           for (;;) {
392                     if (*str == '-') {
393                               *buf++ = '_';
394                               *buf++ = '_';
395                               str++;
396                     } else if ((*buf++ = *str++) == '\0') {
397                               break;
398                     }
399           }
400 }
401 
402 /*ARGSUSED*/
403 static int
dt_header_decl(dt_idhash_t * dhp,dt_ident_t * idp,void * data)404 dt_header_decl(dt_idhash_t *dhp, dt_ident_t *idp, void *data)
405 {
406           dt_header_info_t *infop = data;
407           dtrace_hdl_t *dtp = infop->dthi_dtp;
408           dt_probe_t *prp = idp->di_data;
409           dt_node_t *dnp;
410           char buf[DT_TYPE_NAMELEN];
411           char *fname;
412           const char *p;
413           int i;
414 
415           p = prp->pr_name;
416           for (i = 0; (p = strchr(p, '-')) != NULL; i++)
417                     p++;
418 
419           fname = alloca(strlen(prp->pr_name) + 1 + i);
420           dt_header_fmt_func(fname, prp->pr_name);
421 
422           if (fprintf(infop->dthi_out, "extern void __dtrace_%s___%s(",
423               infop->dthi_pfname, fname) < 0)
424                     return (dt_set_errno(dtp, errno));
425 
426           for (dnp = prp->pr_nargs, i = 0; dnp != NULL; dnp = dnp->dn_list, i++) {
427                     if (fprintf(infop->dthi_out, "%s",
428                         ctf_type_name(dnp->dn_ctfp, dnp->dn_type,
429                         buf, sizeof (buf))) < 0)
430                               return (dt_set_errno(dtp, errno));
431 
432                     if (i + 1 != prp->pr_nargc &&
433                         fprintf(infop->dthi_out, ", ") < 0)
434                               return (dt_set_errno(dtp, errno));
435           }
436 
437           if (i == 0 && fprintf(infop->dthi_out, "void") < 0)
438                     return (dt_set_errno(dtp, errno));
439 
440           if (fprintf(infop->dthi_out, ");\n") < 0)
441                     return (dt_set_errno(dtp, errno));
442 
443           if (fprintf(infop->dthi_out,
444               "#ifndef\t__sparc\n"
445               "extern int __dtraceenabled_%s___%s(void);\n"
446               "#else\n"
447               "extern int __dtraceenabled_%s___%s(long);\n"
448               "#endif\n",
449               infop->dthi_pfname, fname, infop->dthi_pfname, fname) < 0)
450                     return (dt_set_errno(dtp, errno));
451 
452           return (0);
453 }
454 
455 /*ARGSUSED*/
456 static int
dt_header_probe(dt_idhash_t * dhp,dt_ident_t * idp,void * data)457 dt_header_probe(dt_idhash_t *dhp, dt_ident_t *idp, void *data)
458 {
459           dt_header_info_t *infop = data;
460           dtrace_hdl_t *dtp = infop->dthi_dtp;
461           dt_probe_t *prp = idp->di_data;
462           char *mname, *fname;
463           const char *p;
464           int i;
465 
466           p = prp->pr_name;
467           for (i = 0; (p = strchr(p, '-')) != NULL; i++)
468                     p++;
469 
470           mname = alloca(strlen(prp->pr_name) + 1);
471           dt_header_fmt_macro(mname, prp->pr_name);
472 
473           fname = alloca(strlen(prp->pr_name) + 1 + i);
474           dt_header_fmt_func(fname, prp->pr_name);
475 
476           if (fprintf(infop->dthi_out, "#define\t%s_%s(",
477               infop->dthi_pmname, mname) < 0)
478                     return (dt_set_errno(dtp, errno));
479 
480           for (i = 0; i < prp->pr_nargc; i++) {
481                     if (fprintf(infop->dthi_out, "arg%d", i) < 0)
482                               return (dt_set_errno(dtp, errno));
483 
484                     if (i + 1 != prp->pr_nargc &&
485                         fprintf(infop->dthi_out, ", ") < 0)
486                               return (dt_set_errno(dtp, errno));
487           }
488 
489           if (!infop->dthi_empty) {
490                     if (fprintf(infop->dthi_out, ") \\\n\t") < 0)
491                               return (dt_set_errno(dtp, errno));
492 
493                     if (fprintf(infop->dthi_out, "__dtrace_%s___%s(",
494                         infop->dthi_pfname, fname) < 0)
495                               return (dt_set_errno(dtp, errno));
496 
497                     for (i = 0; i < prp->pr_nargc; i++) {
498                               if (fprintf(infop->dthi_out, "arg%d", i) < 0)
499                                         return (dt_set_errno(dtp, errno));
500 
501                               if (i + 1 != prp->pr_nargc &&
502                                   fprintf(infop->dthi_out, ", ") < 0)
503                                         return (dt_set_errno(dtp, errno));
504                     }
505           }
506 
507           if (fprintf(infop->dthi_out, ")\n") < 0)
508                     return (dt_set_errno(dtp, errno));
509 
510           if (!infop->dthi_empty) {
511                     if (fprintf(infop->dthi_out,
512                         "#ifndef\t__sparc\n"
513                         "#define\t%s_%s_ENABLED() \\\n"
514                         "\t__dtraceenabled_%s___%s()\n"
515                         "#else\n"
516                         "#define\t%s_%s_ENABLED() \\\n"
517                         "\t__dtraceenabled_%s___%s(0)\n"
518                         "#endif\n",
519                         infop->dthi_pmname, mname,
520                         infop->dthi_pfname, fname,
521                         infop->dthi_pmname, mname,
522                         infop->dthi_pfname, fname) < 0)
523                               return (dt_set_errno(dtp, errno));
524 
525           } else {
526                     if (fprintf(infop->dthi_out, "#define\t%s_%s_ENABLED() (0)\n",
527                         infop->dthi_pmname, mname) < 0)
528                               return (dt_set_errno(dtp, errno));
529           }
530 
531           return (0);
532 }
533 
534 static int
dt_header_provider(dtrace_hdl_t * dtp,dt_provider_t * pvp,FILE * out)535 dt_header_provider(dtrace_hdl_t *dtp, dt_provider_t *pvp, FILE *out)
536 {
537           dt_header_info_t info;
538           const char *p;
539           int i;
540 
541           if (pvp->pv_flags & DT_PROVIDER_IMPL)
542                     return (0);
543 
544           /*
545            * Count the instances of the '-' character since we'll need to double
546            * those up.
547            */
548           p = pvp->pv_desc.dtvd_name;
549           for (i = 0; (p = strchr(p, '-')) != NULL; i++)
550                     p++;
551 
552           info.dthi_dtp = dtp;
553           info.dthi_out = out;
554           info.dthi_empty = 0;
555 
556           info.dthi_pmname = alloca(strlen(pvp->pv_desc.dtvd_name) + 1);
557           dt_header_fmt_macro(info.dthi_pmname, pvp->pv_desc.dtvd_name);
558 
559           info.dthi_pfname = alloca(strlen(pvp->pv_desc.dtvd_name) + 1 + i);
560           dt_header_fmt_func(info.dthi_pfname, pvp->pv_desc.dtvd_name);
561 
562 #if defined(__FreeBSD__) || defined(__NetBSD__)
563           if (fprintf(out, "#include <sys/sdt.h>\n\n") < 0)
564                     return (dt_set_errno(dtp, errno));
565 #endif
566           if (fprintf(out, "#if _DTRACE_VERSION\n\n") < 0)
567                     return (dt_set_errno(dtp, errno));
568 
569           if (dt_idhash_iter(pvp->pv_probes, dt_header_probe, &info) != 0)
570                     return (-1); /* dt_errno is set for us */
571           if (fprintf(out, "\n\n") < 0)
572                     return (dt_set_errno(dtp, errno));
573           if (dt_idhash_iter(pvp->pv_probes, dt_header_decl, &info) != 0)
574                     return (-1); /* dt_errno is set for us */
575 
576           if (fprintf(out, "\n#else\n\n") < 0)
577                     return (dt_set_errno(dtp, errno));
578 
579           info.dthi_empty = 1;
580 
581           if (dt_idhash_iter(pvp->pv_probes, dt_header_probe, &info) != 0)
582                     return (-1); /* dt_errno is set for us */
583 
584           if (fprintf(out, "\n#endif\n\n") < 0)
585                     return (dt_set_errno(dtp, errno));
586 
587           return (0);
588 }
589 
590 int
dtrace_program_header(dtrace_hdl_t * dtp,FILE * out,const char * fname)591 dtrace_program_header(dtrace_hdl_t *dtp, FILE *out, const char *fname)
592 {
593           dt_provider_t *pvp;
594           char *mfname, *p;
595 
596           if (fname != NULL) {
597                     if ((p = strrchr(fname, '/')) != NULL)
598                               fname = p + 1;
599 
600                     mfname = alloca(strlen(fname) + 1);
601                     dt_header_fmt_macro(mfname, fname);
602                     if (fprintf(out, "#ifndef\t_%s\n#define\t_%s\n\n",
603                         mfname, mfname) < 0)
604                               return (dt_set_errno(dtp, errno));
605           }
606 
607           if (fprintf(out, "#include <unistd.h>\n\n") < 0)
608                     return (-1);
609 
610           if (fprintf(out, "#ifdef\t__cplusplus\nextern \"C\" {\n#endif\n\n") < 0)
611                     return (-1);
612 
613           for (pvp = dt_list_next(&dtp->dt_provlist);
614               pvp != NULL; pvp = dt_list_next(pvp)) {
615                     if (dt_header_provider(dtp, pvp, out) != 0)
616                               return (-1); /* dt_errno is set for us */
617           }
618 
619           if (fprintf(out, "\n#ifdef\t__cplusplus\n}\n#endif\n") < 0)
620                     return (dt_set_errno(dtp, errno));
621 
622           if (fname != NULL && fprintf(out, "\n#endif\t/* _%s */\n", mfname) < 0)
623                     return (dt_set_errno(dtp, errno));
624 
625           return (0);
626 }
627