Sun Dec 8 10:25:38 2019 UTC ()
Pull up following revision(s) (requested by riastradh in ticket #1717):

	sys/dev/cons.c: revision 1.76
	sys/dev/cons.c: revision 1.77

Fix reference count leak in cons(4).
Don't forget to vrele after you're done, folks!
Restore historical $Hdr$ tag after git cvsexportcommit nixed it.


(martin)
diff -r1.72.2.1 -r1.72.2.2 src/sys/dev/cons.c

cvs diff -r1.72.2.1 -r1.72.2.2 src/sys/dev/cons.c (switch to unified diff)

--- src/sys/dev/cons.c 2015/03/09 08:00:46 1.72.2.1
+++ src/sys/dev/cons.c 2019/12/08 10:25:38 1.72.2.2
@@ -1,396 +1,397 @@ @@ -1,396 +1,397 @@
1/* $NetBSD: cons.c,v 1.72.2.1 2015/03/09 08:00:46 snj Exp $ */ 1/* $NetBSD: cons.c,v 1.72.2.2 2019/12/08 10:25:38 martin Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 1988 University of Utah. 4 * Copyright (c) 1988 University of Utah.
5 * Copyright (c) 1990, 1993 5 * Copyright (c) 1990, 1993
6 * The Regents of the University of California. All rights reserved. 6 * The Regents of the University of California. All rights reserved.
7 * 7 *
8 * This code is derived from software contributed to Berkeley by 8 * This code is derived from software contributed to Berkeley by
9 * the Systems Programming Group of the University of Utah Computer 9 * the Systems Programming Group of the University of Utah Computer
10 * Science Department. 10 * Science Department.
11 * 11 *
12 * Redistribution and use in source and binary forms, with or without 12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions 13 * modification, are permitted provided that the following conditions
14 * are met: 14 * are met:
15 * 1. Redistributions of source code must retain the above copyright 15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer. 16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright 17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the 18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution. 19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors 20 * 3. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software 21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission. 22 * without specific prior written permission.
23 * 23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE. 34 * SUCH DAMAGE.
35 * 35 *
36 * from: Utah $Hdr: cons.c 1.7 92/01/21$ 36 * from: Utah $Hdr: cons.c 1.7 92/01/21$
37 * 37 *
38 * @(#)cons.c 8.2 (Berkeley) 1/12/94 38 * @(#)cons.c 8.2 (Berkeley) 1/12/94
39 */ 39 */
40 40
41#include <sys/cdefs.h> 41#include <sys/cdefs.h>
42__KERNEL_RCSID(0, "$NetBSD: cons.c,v 1.72.2.1 2015/03/09 08:00:46 snj Exp $"); 42__KERNEL_RCSID(0, "$NetBSD: cons.c,v 1.72.2.2 2019/12/08 10:25:38 martin Exp $");
43 43
44#include <sys/param.h> 44#include <sys/param.h>
45#include <sys/proc.h> 45#include <sys/proc.h>
46#include <sys/systm.h> 46#include <sys/systm.h>
47#include <sys/buf.h> 47#include <sys/buf.h>
48#include <sys/ioctl.h> 48#include <sys/ioctl.h>
49#include <sys/poll.h> 49#include <sys/poll.h>
50#include <sys/tty.h> 50#include <sys/tty.h>
51#include <sys/file.h> 51#include <sys/file.h>
52#include <sys/conf.h> 52#include <sys/conf.h>
53#include <sys/vnode.h> 53#include <sys/vnode.h>
54#include <sys/kauth.h> 54#include <sys/kauth.h>
55#include <sys/mutex.h> 55#include <sys/mutex.h>
56 56
57#include <dev/cons.h> 57#include <dev/cons.h>
58 58
59dev_type_open(cnopen); 59dev_type_open(cnopen);
60dev_type_close(cnclose); 60dev_type_close(cnclose);
61dev_type_read(cnread); 61dev_type_read(cnread);
62dev_type_write(cnwrite); 62dev_type_write(cnwrite);
63dev_type_ioctl(cnioctl); 63dev_type_ioctl(cnioctl);
64dev_type_poll(cnpoll); 64dev_type_poll(cnpoll);
65dev_type_kqfilter(cnkqfilter); 65dev_type_kqfilter(cnkqfilter);
66 66
67static bool cn_redirect(dev_t *, int, int *); 67static bool cn_redirect(dev_t *, int, int *);
68 68
69const struct cdevsw cons_cdevsw = { 69const struct cdevsw cons_cdevsw = {
70 .d_open = cnopen, 70 .d_open = cnopen,
71 .d_close = cnclose, 71 .d_close = cnclose,
72 .d_read = cnread, 72 .d_read = cnread,
73 .d_write = cnwrite, 73 .d_write = cnwrite,
74 .d_ioctl = cnioctl, 74 .d_ioctl = cnioctl,
75 .d_stop = nostop, 75 .d_stop = nostop,
76 .d_tty = notty, 76 .d_tty = notty,
77 .d_poll = cnpoll, 77 .d_poll = cnpoll,
78 .d_mmap = nommap, 78 .d_mmap = nommap,
79 .d_kqfilter = cnkqfilter, 79 .d_kqfilter = cnkqfilter,
80 .d_discard = nodiscard, 80 .d_discard = nodiscard,
81 .d_flag = D_TTY 81 .d_flag = D_TTY
82}; 82};
83 83
84struct tty *constty = NULL; /* virtual console output device */ 84struct tty *constty = NULL; /* virtual console output device */
85struct consdev *cn_tab; /* physical console device info */ 85struct consdev *cn_tab; /* physical console device info */
86struct vnode *cn_devvp[2]; /* vnode for underlying device. */ 86struct vnode *cn_devvp[2]; /* vnode for underlying device. */
87 87
88int 88int
89cnopen(dev_t dev, int flag, int mode, struct lwp *l) 89cnopen(dev_t dev, int flag, int mode, struct lwp *l)
90{ 90{
91 dev_t cndev; 91 dev_t cndev;
92 int unit, error; 92 int unit, error;
93 93
94 unit = minor(dev); 94 unit = minor(dev);
95 if (unit > 1) 95 if (unit > 1)
96 return ENODEV; 96 return ENODEV;
97 97
98 if (cn_tab == NULL) 98 if (cn_tab == NULL)
99 return (0); 99 return (0);
100 100
101 /* 101 /*
102 * always open the 'real' console device, so we don't get nailed 102 * always open the 'real' console device, so we don't get nailed
103 * later. This follows normal device semantics; they always get 103 * later. This follows normal device semantics; they always get
104 * open() calls. 104 * open() calls.
105 */ 105 */
106 cndev = cn_tab->cn_dev; 106 cndev = cn_tab->cn_dev;
107 if (cndev == NODEV) { 107 if (cndev == NODEV) {
108 /* 108 /*
109 * This is most likely an error in the console attach 109 * This is most likely an error in the console attach
110 * code. Panicking looks better than jumping into nowhere 110 * code. Panicking looks better than jumping into nowhere
111 * through cdevsw below.... 111 * through cdevsw below....
112 */ 112 */
113 panic("cnopen: no console device"); 113 panic("cnopen: no console device");
114 } 114 }
115 if (dev == cndev) { 115 if (dev == cndev) {
116 /* 116 /*
117 * This causes cnopen() to be called recursively, which 117 * This causes cnopen() to be called recursively, which
118 * is generally a bad thing. It is often caused when 118 * is generally a bad thing. It is often caused when
119 * dev == 0 and cn_dev has not been set, but was probably 119 * dev == 0 and cn_dev has not been set, but was probably
120 * initialised to 0. 120 * initialised to 0.
121 */ 121 */
122 panic("cnopen: cn_tab->cn_dev == dev"); 122 panic("cnopen: cn_tab->cn_dev == dev");
123 } 123 }
124 if (cn_devvp[unit] != NULLVP) 124 if (cn_devvp[unit] != NULLVP)
125 return 0; 125 return 0;
126 if ((error = cdevvp(cndev, &cn_devvp[unit])) != 0) 126 if ((error = cdevvp(cndev, &cn_devvp[unit])) != 0)
127 printf("cnopen: unable to get vnode reference\n"); 127 printf("cnopen: unable to get vnode reference\n");
128 error = vn_lock(cn_devvp[unit], LK_EXCLUSIVE | LK_RETRY); 128 error = vn_lock(cn_devvp[unit], LK_EXCLUSIVE | LK_RETRY);
129 if (error == 0) { 129 if (error == 0) {
130 error = VOP_OPEN(cn_devvp[unit], flag, kauth_cred_get()); 130 error = VOP_OPEN(cn_devvp[unit], flag, kauth_cred_get());
131 VOP_UNLOCK(cn_devvp[unit]); 131 VOP_UNLOCK(cn_devvp[unit]);
132 } 132 }
133 return error; 133 return error;
134} 134}
135 135
136int 136int
137cnclose(dev_t dev, int flag, int mode, struct lwp *l) 137cnclose(dev_t dev, int flag, int mode, struct lwp *l)
138{ 138{
139 struct vnode *vp; 139 struct vnode *vp;
140 int unit, error; 140 int unit, error;
141 141
142 unit = minor(dev); 142 unit = minor(dev);
143 143
144 if (cn_tab == NULL) 144 if (cn_tab == NULL)
145 return (0); 145 return (0);
146 146
147 vp = cn_devvp[unit]; 147 vp = cn_devvp[unit];
148 cn_devvp[unit] = NULL; 148 cn_devvp[unit] = NULL;
149 error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 149 error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
150 if (error == 0) { 150 if (error == 0) {
151 error = VOP_CLOSE(vp, flag, kauth_cred_get()); 151 error = VOP_CLOSE(vp, flag, kauth_cred_get());
152 VOP_UNLOCK(vp); 152 VOP_UNLOCK(vp);
 153 vrele(vp);
153 } 154 }
154 return error; 155 return error;
155} 156}
156 157
157int 158int
158cnread(dev_t dev, struct uio *uio, int flag) 159cnread(dev_t dev, struct uio *uio, int flag)
159{ 160{
160 int error; 161 int error;
161 162
162 /* 163 /*
163 * If we would redirect input, punt. This will keep strange 164 * If we would redirect input, punt. This will keep strange
164 * things from happening to people who are using the real 165 * things from happening to people who are using the real
165 * console. Nothing should be using /dev/console for 166 * console. Nothing should be using /dev/console for
166 * input (except a shell in single-user mode, but then, 167 * input (except a shell in single-user mode, but then,
167 * one wouldn't TIOCCONS then). 168 * one wouldn't TIOCCONS then).
168 */ 169 */
169 if (!cn_redirect(&dev, 1, &error)) 170 if (!cn_redirect(&dev, 1, &error))
170 return error; 171 return error;
171 return cdev_read(dev, uio, flag); 172 return cdev_read(dev, uio, flag);
172} 173}
173 174
174int 175int
175cnwrite(dev_t dev, struct uio *uio, int flag) 176cnwrite(dev_t dev, struct uio *uio, int flag)
176{ 177{
177 int error; 178 int error;
178 179
179 /* Redirect output, if that's appropriate. */ 180 /* Redirect output, if that's appropriate. */
180 if (!cn_redirect(&dev, 0, &error)) 181 if (!cn_redirect(&dev, 0, &error))
181 return error; 182 return error;
182 return cdev_write(dev, uio, flag); 183 return cdev_write(dev, uio, flag);
183} 184}
184 185
185int 186int
186cnioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 187cnioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
187{ 188{
188 int error; 189 int error;
189 190
190 error = 0; 191 error = 0;
191 192
192 /* 193 /*
193 * Superuser can always use this to wrest control of console 194 * Superuser can always use this to wrest control of console
194 * output from the "virtual" console. 195 * output from the "virtual" console.
195 */ 196 */
196 if (cmd == TIOCCONS && constty != NULL) { 197 if (cmd == TIOCCONS && constty != NULL) {
197 error = kauth_authorize_device_tty(l->l_cred, 198 error = kauth_authorize_device_tty(l->l_cred,
198 KAUTH_DEVICE_TTY_VIRTUAL, constty); 199 KAUTH_DEVICE_TTY_VIRTUAL, constty);
199 if (!error) 200 if (!error)
200 constty = NULL; 201 constty = NULL;
201 return (error); 202 return (error);
202 } 203 }
203 204
204 /* 205 /*
205 * Redirect the ioctl, if that's appropriate. 206 * Redirect the ioctl, if that's appropriate.
206 * Note that strange things can happen, if a program does 207 * Note that strange things can happen, if a program does
207 * ioctls on /dev/console, then the console is redirected 208 * ioctls on /dev/console, then the console is redirected
208 * out from under it. 209 * out from under it.
209 */ 210 */
210 if (!cn_redirect(&dev, 0, &error)) 211 if (!cn_redirect(&dev, 0, &error))
211 return error; 212 return error;
212 return cdev_ioctl(dev, cmd, data, flag, l); 213 return cdev_ioctl(dev, cmd, data, flag, l);
213} 214}
214 215
215/*ARGSUSED*/ 216/*ARGSUSED*/
216int 217int
217cnpoll(dev_t dev, int events, struct lwp *l) 218cnpoll(dev_t dev, int events, struct lwp *l)
218{ 219{
219 int error; 220 int error;
220 221
221 /* 222 /*
222 * Redirect the poll, if that's appropriate. 223 * Redirect the poll, if that's appropriate.
223 * I don't want to think of the possible side effects 224 * I don't want to think of the possible side effects
224 * of console redirection here. 225 * of console redirection here.
225 */ 226 */
226 if (!cn_redirect(&dev, 0, &error)) 227 if (!cn_redirect(&dev, 0, &error))
227 return POLLHUP; 228 return POLLHUP;
228 return cdev_poll(dev, events, l); 229 return cdev_poll(dev, events, l);
229} 230}
230 231
231/*ARGSUSED*/ 232/*ARGSUSED*/
232int 233int
233cnkqfilter(dev_t dev, struct knote *kn) 234cnkqfilter(dev_t dev, struct knote *kn)
234{ 235{
235 int error; 236 int error;
236 237
237 /* 238 /*
238 * Redirect the kqfilter, if that's appropriate. 239 * Redirect the kqfilter, if that's appropriate.
239 * I don't want to think of the possible side effects 240 * I don't want to think of the possible side effects
240 * of console redirection here. 241 * of console redirection here.
241 */ 242 */
242 if (!cn_redirect(&dev, 0, &error)) 243 if (!cn_redirect(&dev, 0, &error))
243 return error; 244 return error;
244 return cdev_kqfilter(dev, kn); 245 return cdev_kqfilter(dev, kn);
245} 246}
246 247
247int 248int
248cngetc(void) 249cngetc(void)
249{ 250{
250 if (cn_tab == NULL) 251 if (cn_tab == NULL)
251 return (0); 252 return (0);
252 int s = splhigh(); 253 int s = splhigh();
253 for (;;) { 254 for (;;) {
254 const int rv = (*cn_tab->cn_getc)(cn_tab->cn_dev); 255 const int rv = (*cn_tab->cn_getc)(cn_tab->cn_dev);
255 if (rv >= 0) { 256 if (rv >= 0) {
256 splx(s); 257 splx(s);
257 return rv; 258 return rv;
258 } 259 }
259 docritpollhooks(); 260 docritpollhooks();
260 } 261 }
261} 262}
262 263
263int 264int
264cngetsn(char *cp, int size) 265cngetsn(char *cp, int size)
265{ 266{
266 char *lp; 267 char *lp;
267 int c, len; 268 int c, len;
268 269
269 cnpollc(1); 270 cnpollc(1);
270 271
271 lp = cp; 272 lp = cp;
272 len = 0; 273 len = 0;
273 for (;;) { 274 for (;;) {
274 c = cngetc(); 275 c = cngetc();
275 switch (c) { 276 switch (c) {
276 case '\n': 277 case '\n':
277 case '\r': 278 case '\r':
278 printf("\n"); 279 printf("\n");
279 *lp++ = '\0'; 280 *lp++ = '\0';
280 cnpollc(0); 281 cnpollc(0);
281 return (len); 282 return (len);
282 case '\b': 283 case '\b':
283 case '\177': 284 case '\177':
284 case '#': 285 case '#':
285 if (len) { 286 if (len) {
286 --len; 287 --len;
287 --lp; 288 --lp;
288 printf("\b \b"); 289 printf("\b \b");
289 } 290 }
290 continue; 291 continue;
291 case '@': 292 case '@':
292 case 'u'&037: /* CTRL-u */ 293 case 'u'&037: /* CTRL-u */
293 len = 0; 294 len = 0;
294 lp = cp; 295 lp = cp;
295 printf("\n"); 296 printf("\n");
296 continue; 297 continue;
297 default: 298 default:
298 if (len + 1 >= size || c < ' ') { 299 if (len + 1 >= size || c < ' ') {
299 printf("\007"); 300 printf("\007");
300 continue; 301 continue;
301 } 302 }
302 printf("%c", c); 303 printf("%c", c);
303 ++len; 304 ++len;
304 *lp++ = c; 305 *lp++ = c;
305 } 306 }
306 } 307 }
307} 308}
308 309
309void 310void
310cnputc(int c) 311cnputc(int c)
311{ 312{
312 313
313 if (cn_tab == NULL) 314 if (cn_tab == NULL)
314 return; 315 return;
315 316
316 if (c) { 317 if (c) {
317 if (c == '\n') { 318 if (c == '\n') {
318 (*cn_tab->cn_putc)(cn_tab->cn_dev, '\r'); 319 (*cn_tab->cn_putc)(cn_tab->cn_dev, '\r');
319 docritpollhooks(); 320 docritpollhooks();
320 } 321 }
321 (*cn_tab->cn_putc)(cn_tab->cn_dev, c); 322 (*cn_tab->cn_putc)(cn_tab->cn_dev, c);
322 } 323 }
323} 324}
324 325
325void 326void
326cnpollc(int on) 327cnpollc(int on)
327{ 328{
328 static int refcount = 0; 329 static int refcount = 0;
329 330
330 if (cn_tab == NULL) 331 if (cn_tab == NULL)
331 return; 332 return;
332 if (!on) 333 if (!on)
333 --refcount; 334 --refcount;
334 if (refcount == 0) 335 if (refcount == 0)
335 (*cn_tab->cn_pollc)(cn_tab->cn_dev, on); 336 (*cn_tab->cn_pollc)(cn_tab->cn_dev, on);
336 if (on) 337 if (on)
337 ++refcount; 338 ++refcount;
338} 339}
339 340
340void 341void
341nullcnpollc(dev_t dev, int on) 342nullcnpollc(dev_t dev, int on)
342{ 343{
343 344
344} 345}
345 346
346void 347void
347cnbell(u_int pitch, u_int period, u_int volume) 348cnbell(u_int pitch, u_int period, u_int volume)
348{ 349{
349 350
350 if (cn_tab == NULL || cn_tab->cn_bell == NULL) 351 if (cn_tab == NULL || cn_tab->cn_bell == NULL)
351 return; 352 return;
352 (*cn_tab->cn_bell)(cn_tab->cn_dev, pitch, period, volume); 353 (*cn_tab->cn_bell)(cn_tab->cn_dev, pitch, period, volume);
353} 354}
354 355
355void 356void
356cnflush(void) 357cnflush(void)
357{ 358{
358 if (cn_tab == NULL || cn_tab->cn_flush == NULL) 359 if (cn_tab == NULL || cn_tab->cn_flush == NULL)
359 return; 360 return;
360 (*cn_tab->cn_flush)(cn_tab->cn_dev); 361 (*cn_tab->cn_flush)(cn_tab->cn_dev);
361} 362}
362 363
363void 364void
364cnhalt(void) 365cnhalt(void)
365{ 366{
366 if (cn_tab == NULL || cn_tab->cn_halt == NULL) 367 if (cn_tab == NULL || cn_tab->cn_halt == NULL)
367 return; 368 return;
368 (*cn_tab->cn_halt)(cn_tab->cn_dev); 369 (*cn_tab->cn_halt)(cn_tab->cn_dev);
369} 370}
370 371
371/* 372/*
372 * Redirect output, if that's appropriate. If there's no real console, 373 * Redirect output, if that's appropriate. If there's no real console,
373 * return ENXIO. 374 * return ENXIO.
374 * 375 *
375 * Call with tty_mutex held. 376 * Call with tty_mutex held.
376 */ 377 */
377static bool 378static bool
378cn_redirect(dev_t *devp, int is_read, int *error) 379cn_redirect(dev_t *devp, int is_read, int *error)
379{ 380{
380 dev_t dev = *devp; 381 dev_t dev = *devp;
381 382
382 *error = ENXIO; 383 *error = ENXIO;
383 if (constty != NULL && minor(dev) == 0 && 384 if (constty != NULL && minor(dev) == 0 &&
384 (cn_tab == NULL || (cn_tab->cn_pri != CN_REMOTE))) { 385 (cn_tab == NULL || (cn_tab->cn_pri != CN_REMOTE))) {
385 if (is_read) { 386 if (is_read) {
386 *error = 0; 387 *error = 0;
387 return false; 388 return false;
388 } 389 }
389 dev = constty->t_dev; 390 dev = constty->t_dev;
390 } else if (cn_tab == NULL) 391 } else if (cn_tab == NULL)
391 return false; 392 return false;
392 else 393 else
393 dev = cn_tab->cn_dev; 394 dev = cn_tab->cn_dev;
394 *devp = dev; 395 *devp = dev;
395 return true; 396 return true;
396} 397}