xref: /illumos-gate/usr/src/cmd/allocate/allocate3.c (revision db874c57ae335a07060499f1492b0d0e2593e26c)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <auth_attr.h>
30 #include <auth_list.h>
31 #include <dirent.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <libintl.h>
35 #include <locale.h>
36 #include <pwd.h>
37 #include <signal.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 
43 #include <bsm/devices.h>
44 #include <bsm/audit_uevents.h>
45 
46 #include <sys/acl.h>
47 #include <sys/file.h>
48 #include <sys/procfs.h>
49 #include <sys/param.h>
50 #include <sys/resource.h>
51 #include <sys/stat.h>
52 #include <sys/time.h>
53 #include <sys/types.h>
54 #include <sys/wait.h>
55 
56 #include "allocate.h"
57 
58 #ifdef	DEBUG
59 #define	dprintf(s, a) (void) fprintf(stderr, s, a)
60 #define	dperror(s) perror(s)
61 #else	/* !DEBUG */
62 #define	dprintf(s, a)
63 #define	dperror(s)
64 #endif	/* DEBUG */
65 
66 #define	EXIT(number) { \
67 	if (optflg & FORCE) \
68 		error = number; \
69 	else \
70 		return (number); \
71 }
72 
73 #define	DEV_ALLOCATED(sbuf)	((sbuf).st_uid != ALLOC_UID || \
74 				((sbuf).st_mode & ~S_IFMT) == ALLOC_MODE)
75 
76 #define	DEVICE_AUTH_SEPARATOR	","
77 #define	PROCFS	"/proc/"
78 
79 extern void audit_allocate_list(char *);
80 extern void audit_allocate_device(char *);
81 
82 extern char	*newenv[];
83 
84 /*
85  * Checks if the specified user has any of the authorizations in the
86  * list of authorizations
87  */
88 
89 static int
90 is_authorized(char *auth_list, uid_t uid)
91 {
92 	char	*auth;
93 	struct passwd *pw;
94 
95 	pw = getpwuid(uid);
96 	if (pw == NULL) {
97 		dprintf("Can't get user info for uid=%d\n", (int)uid);
98 		return (0);
99 	}
100 
101 	auth = strtok(auth_list, DEVICE_AUTH_SEPARATOR);
102 	while (auth != NULL) {
103 		if (chkauthattr(auth, pw->pw_name))
104 			return (1);
105 		auth = strtok(NULL, DEVICE_AUTH_SEPARATOR);
106 	}
107 	return (0);
108 }
109 
110 static int
111 check_devs(char *list)
112 {
113 	char	*file;
114 
115 	file = strtok(list, " ");
116 	while (file != NULL) {
117 
118 		if (access(file, F_OK) == -1) {
119 			dprintf("Unable to access file %s\n", file);
120 			return (-1);
121 		}
122 		file = strtok(NULL, " ");
123 	}
124 	return (0);
125 }
126 
127 static void
128 print_dev(devmap_t *dev_list)
129 {
130 	char	*file;
131 
132 	(void) printf(gettext("device: %s "), dev_list->dmap_devname);
133 	(void) printf(gettext("type: %s "), dev_list->dmap_devtype);
134 	(void) printf(gettext("files: "));
135 
136 	file = strtok(dev_list->dmap_devlist, " ");
137 	while (file != NULL) {
138 		(void) printf("%s ", file);
139 		file = strtok(NULL, " ");
140 	}
141 	(void) printf("\n");
142 }
143 
144 static int
145 list_device(int optflg, uid_t uid, char *device)
146 {
147 	devalloc_t *dev_ent;
148 	devmap_t *dev_list;
149 	char	file_name[MAXPATHLEN];
150 	struct	stat stat_buf;
151 	char	*list;
152 	int	bytes_formated;
153 
154 	if ((dev_ent = getdanam(device)) == NULL) {
155 		if ((dev_list = getdmapdev(device)) == NULL) {
156 			dprintf("Unable to find %s in the allocate database\n",
157 			    device);
158 			return (NODMAPENT);
159 		} else if ((dev_ent = getdanam(dev_list->dmap_devname)) ==
160 		    NULL) {
161 			dprintf("Unable to find %s in the allocate database\n",
162 			    device);
163 			return (NODAENT);
164 		}
165 	} else if ((dev_list = getdmapnam(device)) == NULL) {
166 		dprintf("Unable to find %s in the allocate database\n", device);
167 		return (NODMAPENT);
168 	}
169 
170 	bytes_formated = snprintf(file_name, MAXPATHLEN, "%s/%s", DAC_DIR,
171 	    dev_ent->da_devname);
172 	if (bytes_formated <= 0) {
173 		return (DEVNAME_ERR);
174 	} else if (bytes_formated >= MAXPATHLEN) {
175 		dprintf("device name %s is too long.\n", dev_ent->da_devname);
176 		return (DEVNAME_TOOLONG);
177 	}
178 
179 	if (stat(file_name, &stat_buf)) {
180 		dprintf("Unable to stat %s\n", file_name);
181 		dperror("Error:");
182 		return (DACACC);
183 	}
184 
185 	if ((optflg & FREE) && DEV_ALLOCATED(stat_buf))
186 		return (ALLOC);
187 
188 	if ((optflg & LIST) && DEV_ALLOCATED(stat_buf) &&
189 	    (stat_buf.st_uid != uid))
190 		return (ALLOC_OTHER);
191 
192 	if ((optflg & CURRENT) && (stat_buf.st_uid != uid))
193 		return (NALLOC);
194 
195 	if ((stat_buf.st_mode & ~S_IFMT) == ALLOC_ERR_MODE)
196 		return (ALLOCERR);
197 
198 	if ((list = strdup(dev_list->dmap_devlist)) == NULL)
199 		return (SYSERROR);
200 
201 	if (check_devs(list) == -1) {
202 		free(list);
203 		return (DSPMISS);
204 	}
205 
206 	print_dev(dev_list);
207 
208 	free(list);
209 	return (0);
210 }
211 
212 int
213 list_devices(int optflg, uid_t uid, char *device)
214 {
215 	DIR   * dev_dir;
216 	struct dirent *dac_file;
217 	int	error = 0, ret_code = 1;
218 
219 	if (optflg & USERID) {
220 		if (!is_authorized(DEVICE_REVOKE_AUTH, getuid()))
221 			return (NOTAUTH);
222 	}
223 	setdaent();
224 
225 	if (device) {
226 		return (list_device(optflg, uid, device));
227 	}
228 
229 	if ((dev_dir = opendir(DAC_DIR)) == NULL) {
230 
231 		dperror("Can't open DAC_DIR");
232 		return (DACACC);
233 	}
234 
235 	while ((dac_file = readdir(dev_dir)) != NULL) {
236 		if ((strcmp(dac_file->d_name, ".") == 0) ||
237 		    (strcmp(dac_file->d_name, "..") == 0)) {
238 			continue;
239 		} else {
240 			error = list_device(optflg, uid, dac_file->d_name);
241 			ret_code = ret_code ? error : ret_code;
242 		}
243 	}
244 	(void) closedir(dev_dir);
245 	enddaent();
246 	return (ret_code);
247 }
248 
249 /*
250  * Set the DAC characteristics of the file.
251  * This uses a fancy chmod() by setting a minimal ACL which sets the mode
252  * and discards any existing ACL.
253  */
254 
255 static int
256 newdac(char *file, uid_t owner, gid_t group, o_mode_t mode)
257 {
258 	int		err = 0;
259 
260 	do {
261 		if (chown(file, owner, group) == -1) {
262 			dperror("newdac, unable to chown");
263 			err = CHOWN_PERR;
264 		}
265 	} while (fdetach(file) == 0);
266 
267 	err = acl_strip(file, owner, group, (mode_t)mode);
268 
269 	if (err != 0) {
270 		dperror("newdac, unable to setacl");
271 		err = SETACL_PERR;
272 	}
273 
274 	return (err);
275 }
276 
277 static int
278 lock_dev(char *file)
279 {
280 	int	fd;
281 
282 	dprintf("locking %s\n", file);
283 	if ((fd = open(file, O_RDWR)) == -1) {
284 		dperror("lock_dev, cannot open DAC file");
285 		return (DACACC);
286 	}
287 
288 	if (lockf(fd, F_TLOCK, 0) == -1) {
289 		dperror("lock_dev, cannot set lock");
290 		return (DACLCK);
291 	}
292 
293 	return (0);
294 }
295 
296 static int
297 mk_alloc(char *list, uid_t uid)
298 {
299 	char	*file;
300 	int	err;
301 
302 	file = strtok(list, " ");
303 	while (file != NULL) {
304 
305 		dprintf("Allocating %s\n", file);
306 		if ((err = newdac(file, uid, getgid(), ALLOC_MODE)) != 0) {
307 			(void) newdac(file, ALLOC_UID, ALLOC_GID,
308 			    ALLOC_ERR_MODE);
309 			return (err);
310 		}
311 
312 		file = strtok(NULL, " ");
313 	}
314 	return (0);
315 }
316 
317 /*
318  * mk_revoke() is used instead of system("/usr/sbin/fuser -k file")
319  * because "/usr/sbin/fuser -k file" kills all processes
320  * working with the file, even "vold" (bug #4095152).
321  */
322 static int
323 mk_revoke(int optflg, char *file)
324 {
325 	char buf[MAXPATHLEN];
326 	int r = 0, p[2], fp, lock;
327 	FILE *ptr;
328 	prpsinfo_t info;
329 	pid_t pid, c_pid;
330 
331 	(void) strcpy(buf, PROCFS);
332 
333 	/*
334 	 * vfork() and execle() just to make the same output
335 	 * as before fixing of bug #4095152.
336 	 * The problem is that the "fuser" command prints
337 	 * one part of output into stderr and another into stdout,
338 	 * but user sees them mixed. Of course, better to change "fuser"
339 	 * or to intercept and not to print its output.
340 	 */
341 	if (!(optflg & SILENT)) {
342 		c_pid = vfork();
343 		if (c_pid == -1)
344 			return (-1);
345 		if (c_pid == 0) {
346 			dprintf("first exec fuser %s\n", file);
347 			(void) execle("/usr/sbin/fuser", "fuser", file, NULL,
348 			    newenv);
349 			dperror("first exec fuser");
350 			_exit(1);
351 		}
352 
353 		(void) waitpid(c_pid, &lock, 0);
354 		dprintf("exit status %x\n", lock);
355 		if (WEXITSTATUS(lock) != 0)
356 			return (-1);
357 	}
358 	dprintf("first continuing c_pid=%d\n", c_pid);
359 
360 	if (pipe(p)) {
361 		dperror("pipe");
362 		return (-1);
363 	}
364 
365 	/* vfork() and execle() to catch output and to process it */
366 	c_pid = vfork();
367 	if (c_pid == -1) {
368 		dperror("second vfork");
369 		return (-1);
370 	}
371 	dprintf("second continuing c_pid=%d\n", c_pid);
372 
373 	if (c_pid == 0) {
374 		(void) close(p[0]);
375 		(void) close(1);
376 		(void) fcntl(p[1], F_DUPFD, 1);
377 		(void) close(p[1]);
378 		(void) close(2);
379 		dprintf("second exec fuser %s\n", file);
380 		(void) execle("/usr/sbin/fuser", "fuser", file, NULL, newenv);
381 		dperror("second exec fuser");
382 		_exit(1);
383 	}
384 
385 	(void) close(p[1]);
386 	if ((ptr = fdopen(p[0], "r")) != NULL) {
387 		while (!feof(ptr)) {
388 			if (fscanf(ptr, "%d", &pid) > 0) {
389 				(void) sprintf(buf + strlen(PROCFS), "%d", pid);
390 				if ((fp = open(buf, O_RDONLY)) == -1) {
391 					dperror(buf);
392 					continue;
393 				}
394 				if (ioctl(fp, PIOCPSINFO, (char *)&info)
395 				    == -1) {
396 					dprintf("%d psinfo failed", pid);
397 					dperror("");
398 					(void) close(fp);
399 					continue;
400 				}
401 				(void) close(fp);
402 				if (strcmp(info.pr_fname, "vold") == NULL) {
403 					dprintf("%d matched vold name\n", pid);
404 					continue;
405 				}
406 				dprintf("killing %s", info.pr_fname);
407 				dprintf("(%d)\n", pid);
408 				if ((r = kill(pid, SIGKILL)) == -1) {
409 					dprintf("kill %d", pid);
410 					dperror("");
411 					break;
412 				}
413 			}
414 		}
415 		dprintf("eof reached %x\n", ptr);
416 	} else {
417 		dperror("fdopen(p[0])");
418 		r = -1;
419 	}
420 
421 	(void) fclose(ptr);
422 	return (r);
423 }
424 
425 static int
426 mk_unalloc(int optflg, char *list)
427 {
428 	char	*file;
429 	int	error = 0;
430 	int child, status;
431 
432 	audit_allocate_list(list);
433 
434 	child = vfork();
435 	switch (child) {
436 	case -1:
437 		return (-1);
438 	case 0:
439 		(void) setuid(0);
440 		file = strtok(list, " ");
441 		while (file != NULL) {
442 			dprintf("Deallocating %s\n", file);
443 			if (mk_revoke(optflg, file) < 0) {
444 				dprintf("mk_unalloc: unable to revoke %s\n",
445 				    file);
446 				dperror("");
447 				error = CNTFRC;
448 				break;
449 			}
450 			error = newdac(file, ALLOC_UID, ALLOC_GID,
451 			    DEALLOC_MODE);
452 			file = strtok(NULL, " ");
453 		}
454 		exit(error);
455 	default:
456 		while (wait(&status) != child);
457 		if (WIFEXITED(status)) {
458 			return (WEXITSTATUS(status));
459 		}
460 		return (-1);
461 	}
462 }
463 
464 static int
465 exec_clean(int optflg, char *name, char *path)
466 {
467 	char	*mode, *cmd;
468 	int	status;
469 	int	c;
470 
471 	if ((optflg & (FORCE_ALL | SILENT)) == (FORCE_ALL | SILENT))
472 		mode = "-I";
473 	else if (optflg & FORCE_ALL)
474 		mode = "-i";
475 	else if (optflg & FORCE)
476 		mode = "-f";
477 	else
478 		mode = "-s";
479 	if ((cmd = strrchr(path, '/')) == NULL)
480 		cmd = path;
481 	else
482 		cmd++;	/* skip leading '/' */
483 
484 	c = vfork();
485 	switch (c) {
486 	case -1:
487 		return (-1);
488 	case 0:
489 		(void) setuid(0);
490 		dprintf("clean script: %s, ", path);
491 		dprintf("cmd=%s, ", cmd);
492 		dprintf("mode=%s, ", mode);
493 		dprintf("name=%s\n", name);
494 		(void) execle(path, cmd, mode, name, NULL, newenv);
495 		dprintf("Unable to execute clean up script %s\n", path);
496 		dperror("");
497 		exit(CNTDEXEC);
498 	default:
499 		while (wait(&status) != c);
500 		if (WIFEXITED(status))
501 			return (WEXITSTATUS(status));
502 		dprintf("exit status %d\n", status);
503 		return (-1);
504 	}
505 }
506 
507 static int
508 deallocate_dev(int optflg, devalloc_t *dev_ent, uid_t uid)
509 {
510 	devmap_t *dev_list;
511 	char	file_name[MAXPATHLEN];
512 	struct stat stat_buf;
513 	char	*list;
514 	int	error = 0, err;
515 	int	bytes_formated;
516 
517 	bytes_formated = snprintf(file_name, MAXPATHLEN, "%s/%s", DAC_DIR,
518 	    dev_ent->da_devname);
519 	if (bytes_formated <= 0) {
520 		return (DEVNAME_ERR);
521 	} else if (bytes_formated >= MAXPATHLEN) {
522 		dprintf("device name %s is too long.\n", dev_ent->da_devname);
523 		return (DEVNAME_TOOLONG);
524 	}
525 
526 	audit_allocate_device(file_name);
527 
528 	if (stat(file_name, &stat_buf)) {
529 		dprintf("Unable to stat %s\n", file_name);
530 		dperror("Error:");
531 		return (DACACC);
532 	}
533 
534 	if (!(optflg & FORCE) && stat_buf.st_uid != uid &&
535 	    DEV_ALLOCATED(stat_buf)) {
536 		return (NALLOCU);
537 	}
538 
539 	if (!(optflg & FORCE_ALL) && !DEV_ALLOCATED(stat_buf)) {
540 		if ((stat_buf.st_mode & ~S_IFMT) == ALLOC_ERR_MODE) {
541 			if (!(optflg & FORCE))
542 				return (ALLOCERR);
543 		} else
544 			return (NALLOC);
545 	}
546 
547 	/* All checks passed, time to lock and deallocate */
548 	if ((error = lock_dev(file_name)) != 0)
549 		return (error);
550 
551 	if ((err = newdac(file_name, ALLOC_UID, ALLOC_GID, DEALLOC_MODE))
552 	    != 0) {
553 		(void) newdac(file_name, ALLOC_UID, ALLOC_GID, ALLOC_ERR_MODE);
554 		EXIT(err);
555 	}
556 
557 	if ((dev_list = getdmapnam(dev_ent->da_devname)) == NULL) {
558 		dprintf("Unable to find %s in the device map database\n",
559 		    dev_ent->da_devname);
560 		EXIT(NODMAPENT);
561 	} else {
562 		if ((list = strdup(dev_list->dmap_devlist)) == NULL) {
563 			EXIT(SYSERROR)
564 		} else {
565 			if (mk_unalloc(optflg, list) != 0) {
566 				(void) newdac(file_name, ALLOC_UID, ALLOC_GID,
567 				    ALLOC_ERR_MODE);
568 				free(list);
569 				list = NULL;
570 				EXIT(DEVLST);
571 			}
572 		}
573 	}
574 
575 	if (list != NULL)
576 		free(list);
577 	if (exec_clean(optflg, dev_ent->da_devname, dev_ent->da_devexec))
578 		EXIT(CLEAN_ERR);
579 	return (error);
580 }
581 
582 static int
583 allocate_dev(int optflg, uid_t uid, devalloc_t *dev_ent)
584 {
585 	devmap_t *dev_list;
586 	char	file_name[MAXPATHLEN];
587 	struct stat stat_buf;
588 	char	*list;
589 	int	error = 0;
590 	int	bytes_formated;
591 
592 	bytes_formated = snprintf(file_name, MAXPATHLEN, "%s/%s", DAC_DIR,
593 	    dev_ent->da_devname);
594 	if (bytes_formated <= 0) {
595 		return (DEVNAME_ERR);
596 	} else if (bytes_formated >= MAXPATHLEN) {
597 		dprintf("device name %s is too long.\n", dev_ent->da_devname);
598 		return (DEVNAME_TOOLONG);
599 	}
600 
601 	audit_allocate_device(file_name);
602 
603 	if (stat(file_name, &stat_buf)) {
604 		dprintf("Unable to stat %s\n", file_name);
605 		dperror("Error:");
606 		return (DACACC);
607 	}
608 
609 	if (DEV_ALLOCATED(stat_buf)) {
610 		if (optflg & FORCE) {
611 			if (deallocate_dev(FORCE, dev_ent, uid)) {
612 				dprintf("Couldn't force deallocate device %s\n",
613 				    dev_ent->da_devname);
614 				return (CNTFRC);
615 			}
616 		} else if (stat_buf.st_uid == uid) {
617 			return (ALLOC);
618 		} else
619 			return (ALLOC_OTHER);
620 	}
621 	if ((stat_buf.st_mode & ~S_IFMT) == ALLOC_ERR_MODE)
622 		return (ALLOCERR);
623 
624 	if (strcmp(dev_ent->da_devauth, "*") == 0) {
625 		dprintf("Device %s is not allocatable\n", dev_ent->da_devname);
626 		return (AUTHERR);
627 	}
628 
629 	if (strcmp(dev_ent->da_devauth, "@")) {
630 		if (!is_authorized(dev_ent->da_devauth, uid)) {
631 			dprintf("User %d is unauthorized to allocate\n",
632 			    (int)uid);
633 			return (IMPORT_ERR);
634 		}
635 	}
636 
637 	if ((dev_list = getdmapnam(dev_ent->da_devname)) == NULL) {
638 		dprintf("Unable to find %s in device map database\n",
639 		    dev_ent->da_devname);
640 		return (NODMAPENT);
641 	}
642 
643 	if ((list = strdup(dev_list->dmap_devlist)) == NULL)
644 		return (SYSERROR);
645 
646 	if (check_devs(list) == -1) {
647 		free(list);
648 		return (DSPMISS);
649 	}
650 
651 	/* All checks passed, time to lock and allocate */
652 	if ((error = lock_dev(file_name)) != 0) {
653 		free(list);
654 		return (error);
655 	}
656 
657 	if ((error = newdac(file_name, uid, getgid(), ALLOC_MODE)) != 0) {
658 		(void) newdac(file_name, ALLOC_UID, ALLOC_GID, ALLOC_ERR_MODE);
659 		free(list);
660 		return (error);
661 	}
662 
663 	/* refresh list from check_devs overwritting it */
664 	(void) strcpy(list, dev_list->dmap_devlist);
665 	audit_allocate_list(list);
666 
667 	if (mk_alloc(list, uid) != 0) {
668 		/* refresh list from mk_alloc overwritting it */
669 		(void) strcpy(list, dev_list->dmap_devlist);
670 		(void) mk_unalloc(optflg, list);
671 		free(list);
672 		return (DEVLST);
673 	}
674 
675 	free(list);
676 	return (0);
677 }
678 
679 int
680 allocate(int optflg, uid_t uid, char *device)
681 {
682 	devalloc_t	*dev_ent;
683 	devmap_t	*dev_list;
684 
685 	if (((optflg & FORCE) || uid != getuid()) &&
686 	    !is_authorized(DEVICE_REVOKE_AUTH, getuid()))
687 		return (NOTAUTH);
688 
689 	setdaent();
690 	setdmapent();
691 
692 	if (!(optflg & TYPE)) {
693 		if ((dev_ent = getdanam(device)) == NULL) {
694 			if ((dev_list = getdmapdev(device)) == NULL)
695 				return (NODMAPENT);
696 			else if ((dev_ent = getdanam(dev_list->dmap_devname))
697 			    == NULL)
698 				return (NODAENT);
699 		}
700 		return (allocate_dev(optflg, uid, dev_ent));
701 	}
702 
703 	while ((dev_ent = getdatype(device)) != NULL) {
704 		dprintf("trying to allocate %s\n", dev_ent->da_devname);
705 		if (!allocate_dev(optflg, uid, dev_ent)) {
706 			return (0);
707 		}
708 	}
709 	enddaent();
710 	return (NO_DEVICE);
711 }
712 
713 int
714 deallocate(int optflg, uid_t uid, char *device)
715 {
716 	DIR	*dev_dir;
717 	struct dirent	*dac_file;
718 	devalloc_t	*dev_ent;
719 	devmap_t	*dev_list;
720 	int	error = NODAENT;
721 
722 	if (optflg & (FORCE | FORCE_ALL) &&
723 	    !is_authorized(DEVICE_REVOKE_AUTH, getuid()))
724 		return (NOTAUTH);
725 	if (optflg & FORCE_ALL)
726 		optflg |= FORCE;
727 
728 	setdaent();
729 	setdmapent();
730 
731 	if (!(optflg & FORCE_ALL)) {
732 		if ((dev_ent = getdanam(device)) == NULL) {
733 			if ((dev_list = getdmapdev(device)) == NULL)
734 				return (NODMAPENT);
735 			else if ((dev_ent = getdanam(dev_list->dmap_devname))
736 			    == NULL)
737 				return (NODAENT);
738 		}
739 
740 		return (deallocate_dev(optflg, dev_ent, uid));
741 	}
742 
743 	if ((dev_dir = opendir(DAC_DIR)) == NULL) {
744 		dperror("Can't open DAC_DIR");
745 		return (DACACC);
746 	}
747 
748 	while ((dac_file = readdir(dev_dir)) != NULL) {
749 		if ((strcmp(dac_file->d_name, ".") == 0) ||
750 		    (strcmp(dac_file->d_name, "..") == 0)) {
751 			continue;
752 		} else {
753 			if ((dev_ent = getdanam(dac_file->d_name)) == NULL) {
754 				continue;
755 			}
756 			error = deallocate_dev(optflg, dev_ent, uid);
757 		}
758 	}
759 	(void) closedir(dev_dir);
760 	enddaent();
761 	return (error);
762 }
763