1 /*        $NetBSD: preen.c,v 1.32 2015/06/21 04:01:40 dholland Exp $  */
2 
3 /*
4  * Copyright (c) 1990, 1993
5  *        The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)preen.c     8.5 (Berkeley) 4/28/95";
36 #else
37 __RCSID("$NetBSD: preen.c,v 1.32 2015/06/21 04:01:40 dholland Exp $");
38 #endif
39 #endif /* not lint */
40 
41 /*
42  * used by sbin/fsck
43  * used by usr.sbin/quotacheck
44  */
45 
46 #include <sys/param.h>
47 #include <sys/stat.h>
48 #include <sys/wait.h>
49 #include <sys/queue.h>
50 #include <sys/disk.h>
51 #include <sys/ioctl.h>
52 
53 #include <err.h>
54 #include <ctype.h>
55 #include <fstab.h>
56 #include <fcntl.h>
57 #include <string.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <unistd.h>
61 #include <util.h>
62 
63 #include "fsutil.h"
64 #include "exitvalues.h"
65 
66 struct partentry {
67           TAILQ_ENTRY(partentry)         p_entries;
68           char                          *p_devname;         /* device name */
69           char                          *p_mntpt; /* mount point */
70           char                          *p_type;  /* file system type */
71           void                          *p_auxarg;          /* auxiliary argument */
72 };
73 
74 TAILQ_HEAD(part, partentry) badh;
75 
76 struct diskentry {
77           TAILQ_ENTRY(diskentry)            d_entries;
78           char                             *d_name;         /* disk base name */
79           TAILQ_HEAD(prt, partentry)  d_part;     /* list of partitions on disk */
80           int                               d_pid;          /* 0 or pid of fsck proc */
81 };
82 
83 TAILQ_HEAD(diskinfo, diskentry) diskh;
84 
85 static int nrun = 0, ndisks = 0;
86 
87 static struct diskentry *finddisk(const char *);
88 static void addpart(const char *, const char *, const char *, void *);
89 static int startdisk(struct diskentry *,
90     int (*)(const char *, const char *, const char *, void *, pid_t *));
91 static void printpart(void);
92 
93 int
checkfstab(int flags,int maxrun,void * (* docheck)(struct fstab *),int (* checkit)(const char *,const char *,const char *,void *,pid_t *))94 checkfstab(int flags, int maxrun, void *(*docheck)(struct fstab *),
95     int (*checkit)(const char *, const char *, const char *, void *, pid_t *))
96 {
97           struct fstab *fs;
98           struct diskentry *d, *nextdisk;
99           struct partentry *p;
100           int ret, pid, retcode, passno, sumstatus, status;
101           void *auxarg;
102           const char *name;
103           int error = FSCK_EXIT_OK;
104 
105           TAILQ_INIT(&badh);
106           TAILQ_INIT(&diskh);
107 
108           sumstatus = FSCK_EXIT_OK;
109 
110           for (passno = 1; passno <= 2; passno++) {
111                     if (setfsent() == 0) {
112                               warnx("Can't open checklist file: %s", _PATH_FSTAB);
113                               return FSCK_EXIT_CHECK_FAILED;
114                     }
115                     while ((fs = getfsent()) != 0) {
116                               char buf[MAXPATHLEN];
117                               const char *fsspec;
118                               if ((auxarg = (*docheck)(fs)) == NULL)
119                                         continue;
120                               fsspec = getfsspecname(buf, sizeof(buf), fs->fs_spec);
121                               if (fsspec == NULL) {
122                                         warn("%s", buf);
123                                         return FSCK_EXIT_CHECK_FAILED;
124                               }
125                               name = blockcheck(fsspec);
126                               if (flags & CHECK_DEBUG)
127                                         printf("pass %d, name %s\n", passno, name);
128 
129                               if ((flags & CHECK_PREEN) == 0 ||
130                                   (passno == 1 && fs->fs_passno == 1)) {
131                                         if (name == NULL) {
132                                                   if (flags & CHECK_PREEN)
133                                                             return FSCK_EXIT_CHECK_FAILED;
134                                                   else
135                                                             continue;
136                                         }
137                                         sumstatus = (*checkit)(fs->fs_vfstype,
138                                             name, fs->fs_file, auxarg, NULL);
139 
140                                         if (sumstatus) {
141                                                   if ((flags & CHECK_NOFIX) == 0)
142                                                             return sumstatus;
143                                                   else if (error < sumstatus)
144                                                             error = sumstatus;
145                                         }
146                               } else if (passno == 2 && fs->fs_passno > 1) {
147                                         if (name == NULL) {
148                                                   (void) fprintf(stderr,
149                                                       "BAD DISK NAME %s\n", fsspec);
150                                                   sumstatus = FSCK_EXIT_CHECK_FAILED;
151                                                   continue;
152                                         }
153                                         addpart(fs->fs_vfstype, name, fs->fs_file,
154                                             auxarg);
155                               }
156                     }
157                     if ((flags & CHECK_PREEN) == 0)
158                               return error;
159           }
160 
161           if (flags & CHECK_DEBUG)
162                     printpart();
163 
164           if (flags & CHECK_PREEN) {
165                     if (maxrun == 0)
166                               maxrun = ndisks;
167                     if (maxrun > ndisks)
168                               maxrun = ndisks;
169                     nextdisk = TAILQ_FIRST(&diskh);
170                     for (passno = 0; passno < maxrun; ++passno) {
171                               if ((ret = startdisk(nextdisk, checkit)) != 0) {
172                                         if ((flags & CHECK_NOFIX) == 0)
173                                                   return ret;
174                                         else if (error < ret)
175                                                   error = ret;
176                               }
177                               nextdisk = TAILQ_NEXT(nextdisk, d_entries);
178                     }
179 
180                     while ((pid = wait(&status)) != -1) {
181                               TAILQ_FOREACH(d, &diskh, d_entries)
182                                         if (d->d_pid == pid)
183                                                   break;
184 
185                               if (d == NULL) {
186                                         warnx("Unknown pid %d", pid);
187                                         continue;
188                               }
189 
190 
191                               if (WIFEXITED(status))
192                                         retcode = WEXITSTATUS(status);
193                               else
194                                         retcode = 0;
195 
196                               p = TAILQ_FIRST(&d->d_part);
197 
198                               if (flags & (CHECK_DEBUG|CHECK_VERBOSE))
199                                         (void) printf("done %s: %s (%s) = 0x%x\n",
200                                             p->p_type, p->p_devname, p->p_mntpt,
201                                             status);
202 
203                               if (WIFSIGNALED(status)) {
204                                         (void) fprintf(stderr,
205                                             "%s: %s (%s): EXITED WITH SIGNAL %d\n",
206                                             p->p_type, p->p_devname, p->p_mntpt,
207                                             WTERMSIG(status));
208                                         retcode = FSCK_EXIT_SIGNALLED;
209                               }
210 
211                               TAILQ_REMOVE(&d->d_part, p, p_entries);
212 
213                               if (retcode != 0) {
214                                         TAILQ_INSERT_TAIL(&badh, p, p_entries);
215                                         sumstatus |= retcode;
216                               } else {
217                                         free(p->p_type);
218                                         free(p->p_devname);
219                                         free(p);
220                               }
221                               d->d_pid = 0;
222                               nrun--;
223 
224                               if (TAILQ_FIRST(&d->d_part) == NULL)
225                                         ndisks--;
226 
227                               if (nextdisk == NULL) {
228                                         if (TAILQ_FIRST(&d->d_part) != NULL) {
229                                                   if ((ret = startdisk(d, checkit)) != 0)
230                                                   {
231                                                             if ((flags & CHECK_NOFIX) == 0)
232                                                                       return ret;
233                                                             else if (error < ret)
234                                                                       error = ret;
235                                                   }
236                                         }
237                               } else if (nrun < maxrun && nrun < ndisks) {
238                                         for ( ;; ) {
239                                                   nextdisk = TAILQ_NEXT(nextdisk,
240                                                       d_entries);
241                                                   if (nextdisk == NULL)
242                                                             nextdisk = TAILQ_FIRST(&diskh);
243                                                   if (TAILQ_FIRST(&nextdisk->d_part)
244                                                       != NULL && nextdisk->d_pid == 0)
245                                                             break;
246                                         }
247                                         if ((ret = startdisk(nextdisk, checkit)) != 0)
248                                         {
249                                                   if ((flags & CHECK_NOFIX) == 0)
250                                                             return ret;
251                                                   else if (error < ret)
252                                                             error = ret;
253                                         }
254                               }
255                     }
256           }
257           if (sumstatus) {
258                     p = TAILQ_FIRST(&badh);
259                     if (p == NULL)
260                               return sumstatus;
261 
262                     (void) fprintf(stderr,
263                               "THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t",
264                               TAILQ_NEXT(p, p_entries) ? "S" : "",
265                               "UNEXPECTED INCONSISTENCY:");
266 
267                     TAILQ_FOREACH(p, &badh, p_entries)
268                               (void) fprintf(stderr,
269                                   "%s: %s (%s)%s", p->p_type, p->p_devname,
270                                   p->p_mntpt, TAILQ_NEXT(p, p_entries) ? ", " : "\n");
271 
272                     return sumstatus;
273           }
274           (void) endfsent();
275           return error;
276 }
277 
278 
279 static struct diskentry *
finddisk(const char * name)280 finddisk(const char *name)
281 {
282           const char *p;
283           size_t len, dlen;
284           struct diskentry *d;
285           char buf[MAXPATHLEN];
286           struct dkwedge_info dkw;
287           int fd;
288 
289           if ((fd = opendisk(name, O_RDONLY, buf, sizeof(buf), 0)) != -1) {
290                     if (ioctl(fd, DIOCGWEDGEINFO, &dkw) != -1)
291                               name = dkw.dkw_parent;
292                     (void)close(fd);
293           }
294 
295           for (dlen = len = strlen(name), p = name + len - 1; p >= name; --p)
296                     if (isdigit((unsigned char)*p)) {
297                               len = p - name + 1;
298                               break;
299                     }
300           if (p < name)
301                     len = dlen;
302 
303           TAILQ_FOREACH(d, &diskh, d_entries)
304                     if (strncmp(d->d_name, name, len) == 0 && d->d_name[len] == 0)
305                               return d;
306 
307           d = emalloc(sizeof(*d));
308           d->d_name = estrdup(name);
309           d->d_name[len] = '\0';
310           TAILQ_INIT(&d->d_part);
311           d->d_pid = 0;
312 
313           TAILQ_INSERT_TAIL(&diskh, d, d_entries);
314           ndisks++;
315 
316           return d;
317 }
318 
319 
320 static void
printpart(void)321 printpart(void)
322 {
323           struct diskentry *d;
324           struct partentry *p;
325 
326           TAILQ_FOREACH(d, &diskh, d_entries) {
327                     (void) printf("disk %s:", d->d_name);
328                     TAILQ_FOREACH(p, &d->d_part, p_entries)
329                               (void) printf(" %s", p->p_devname);
330                     (void) printf("\n");
331           }
332 }
333 
334 
335 static void
addpart(const char * type,const char * dev,const char * mntpt,void * auxarg)336 addpart(const char *type, const char *dev, const char *mntpt, void *auxarg)
337 {
338           struct diskentry *d = finddisk(dev);
339           struct partentry *p;
340 
341           TAILQ_FOREACH(p, &d->d_part, p_entries)
342                     if (strcmp(p->p_devname, dev) == 0) {
343                               warnx("%s in fstab more than once!", dev);
344                               return;
345                     }
346 
347           p = emalloc(sizeof(*p));
348           p->p_devname = estrdup(dev);
349           p->p_mntpt = estrdup(mntpt);
350           p->p_type = estrdup(type);
351           p->p_auxarg = auxarg;
352 
353           TAILQ_INSERT_TAIL(&d->d_part, p, p_entries);
354 }
355 
356 
357 static int
startdisk(struct diskentry * d,int (* checkit)(const char *,const char *,const char *,void *,pid_t *))358 startdisk(struct diskentry *d,
359     int (*checkit)(const char *, const char *, const char *, void *, pid_t *))
360 {
361           struct partentry *p = TAILQ_FIRST(&d->d_part);
362           int rv;
363 
364           while ((rv = (*checkit)(p->p_type, p->p_devname, p->p_mntpt,
365               p->p_auxarg, &d->d_pid)) != 0 && nrun > 0)
366                     sleep(10);
367 
368           if (rv == 0)
369                     nrun++;
370 
371           return rv;
372 }
373