from amd5.3 alpha11
[unix-history] / usr / src / usr.sbin / amd / fsinfo / fsi_analyze.c
CommitLineData
0ee7a6b2
JSP
1/*
2 * $Id: fsi_analyze.c,v 5.2.1.2 90/12/21 16:46:44 jsp Alpha $
3 *
4 * Copyright (c) 1989 Jan-Simon Pendry
5 * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
6 * Copyright (c) 1989 The Regents of the University of California.
7 * All rights reserved.
8 *
9 * This code is derived from software contributed to Berkeley by
10 * Jan-Simon Pendry at Imperial College, London.
11 *
2f619045 12 * %sccs.include.redist.c%
0ee7a6b2 13 *
2f619045 14 * @(#)fsi_analyze.c 5.2 (Berkeley) %G%
0ee7a6b2
JSP
15 *
16 */
17
18/*
19 * Analyze filesystem declarations
20 *
21 * Note: most of this is magic!
22 */
23
24#include "../fsinfo/fsinfo.h"
25
26char *disk_fs_strings[] = {
27 "fstype", "opts", "dumpset", "passno", "freq", "mount", "log", 0,
28};
29
30char *mount_strings[] = {
31 "volname", "exportfs", 0,
32};
33
34char *fsmount_strings[] = {
35 "as", "volname", "fstype", "opts", "from", 0,
36};
37
38char *host_strings[] = {
39 "host", "netif", "config", "arch", "cluster", "os", 0,
40};
41
42char *ether_if_strings[] = {
43 "inaddr", "netmask", "hwaddr", 0,
44};
45
46/*
47 * Strip off the trailing part of a domain
48 * to produce a short-form domain relative
49 * to the local host domain.
50 * Note that this has no effect if the domain
51 * names do not have the same number of
52 * components. If that restriction proves
53 * to be a problem then the loop needs recoding
54 * to skip from right to left and do partial
55 * matches along the way -- ie more expensive.
56 */
57void domain_strip(otherdom, localdom)
58char *otherdom, *localdom;
59{
60#ifdef PARTIAL_DOMAINS
61 char *p1 = otherdom-1;
62 char *p2 = localdom-1;
63
64 do {
65 if (p1 = strchr(p1+1, '.'))
66 if (p2 = strchr(p2+1, '.'))
67 if (STREQ(p1+1, p2+1)) {
68 *p1 = '\0';
69 break;
70 }
71 } while (p1 && p2);
72#else
73 char *p1, *p2;
74
75 if ((p1 = strchr(otherdom, '.')) &&
76 (p2 = strchr(localdom, '.')) &&
77 (strcmp(p1+1, p2+1) == 0))
78 *p1 = '\0';
79#endif /* PARTIAL_DOMAINS */
80}
81
82/*
83 * Take a little-endian domain name and
84 * transform into a big-endian Un*x pathname.
85 * For example: kiska.doc.ic -> ic/doc/kiska
86 */
87static char *compute_hostpath(hn)
88char *hn;
89{
90 char *p = strdup(hn);
91 char *d;
92 char path[MAXPATHLEN];
93
94 domain_strip(p, hostname);
95 path[0] = '\0';
96
97 do {
98 d = strrchr(p, '.');
99 if (d) {
100 *d = 0;
101 strcat(path, d+1);
102 strcat(path, "/");
103 } else {
104 strcat(path, p);
105 }
106 } while (d);
107
108 log("hostpath of '%s' is '%s'", hn, path);
109
110 strcpy(p, path);
111 return p;
112}
113
114static dict_ent *find_volname(nn)
115char *nn;
116{
117 dict_ent *de;
118 char *p = strdup(nn);
119 char *q;
120
121 do {
122 log("Searching for volname %s", p);
123 de = dict_locate(dict_of_volnames, p);
124 q = strrchr(p, '/');
125 if (q) *q = '\0';
126 } while (!de && q);
127
128 free(p);
129 return de;
130}
131
132static show_required(l, mask, info, hostname, strings)
133ioloc *l;
134int mask;
135char *info;
136char *hostname;
137char *strings[];
138{
139 int i;
140 log("mask left for %s:%s is %#x", hostname, info, mask);
141
142 for (i = 0; strings[i]; i++)
143 if (ISSET(mask, i))
144 lerror(l, "%s:%s needs field \"%s\"", hostname, info, strings[i]);
145}
146
147/*
148 * Check and fill in "exportfs" details.
149 * Make sure the m_exported field references
150 * the most local node with an "exportfs" entry.
151 */
152static int check_exportfs(q, e)
153qelem *q;
154mount *e;
155{
156 mount *mp;
157 int errors = 0;
158
159 ITER(mp, mount, q) {
160 if (ISSET(mp->m_mask, DM_EXPORTFS)) {
161 if (e)
162 lwarning(mp->m_ioloc, "%s has duplicate exportfs data", mp->m_name);
163 mp->m_exported = mp;
164 if (!ISSET(mp->m_mask, DM_VOLNAME))
165 set_mount(mp, DM_VOLNAME, strdup(mp->m_name));
166 } else {
167 mp->m_exported = e;
168 }
169
170 /*
171 * Recursively descend the mount tree
172 */
173 if (mp->m_mount)
174 errors += check_exportfs(mp->m_mount, mp->m_exported);
175
176 /*
177 * If a volume name has been specified, but this node and none
178 * of its parents has been exported, report an error.
179 */
180 if (ISSET(mp->m_mask, DM_VOLNAME) && !mp->m_exported) {
181 lerror(mp->m_ioloc, "%s has a volname but no exportfs data", mp->m_name);
182 errors++;
183 }
184 }
185
186 return errors;
187}
188
189static int analyze_dkmount_tree(q, parent, dk)
190qelem *q;
191mount *parent;
192disk_fs *dk;
193{
194 mount *mp;
195 int errors = 0;
196
197 ITER(mp, mount, q) {
198 log("Mount %s:", mp->m_name);
199 if (parent) {
200 char n[MAXPATHLEN];
201 sprintf(n, "%s/%s", parent->m_name, mp->m_name);
202 if (*mp->m_name == '/')
203 lerror(mp->m_ioloc, "sub-directory %s of %s starts with '/'", mp->m_name, parent->m_name);
204 else if (STREQ(mp->m_name, "default"))
205 lwarning(mp->m_ioloc, "sub-directory of %s is named \"default\"", parent->m_name);
206 log("Changing name %s to %s", mp->m_name, n);
207 free(mp->m_name);
208 mp->m_name = strdup(n);
209 }
210 mp->m_name_len = strlen(mp->m_name);
211 mp->m_parent = parent;
212 mp->m_dk = dk;
213 if (mp->m_mount)
214 analyze_dkmount_tree(mp->m_mount, mp, dk);
215 }
216
217 return errors;
218}
219
220/*
221 * The mount tree is a singleton list
222 * containing the top-level mount
223 * point for a disk.
224 */
225static int analyze_dkmounts(dk, q)
226disk_fs *dk;
227qelem *q;
228{
229 int errors = 0;
230 mount *mp, *mp2 = 0;
231 int i = 0;
232
233 /*
234 * First scan the list of subdirs to make
235 * sure there is only one - and remember it
236 */
237 if (q) {
238 ITER(mp, mount, q) {
239 mp2 = mp;
240 i++;
241 }
242 }
243
244 /*
245 * Check...
246 */
247 if (i < 1) {
248 lerror(dk->d_ioloc, "%s:%s has no mount point", dk->d_host->h_hostname, dk->d_dev);
249 return 1;
250 }
251 if (i > 1) {
252 lerror(dk->d_ioloc, "%s:%s has more than one mount point", dk->d_host->h_hostname, dk->d_dev);
253 errors++;
254 }
255 /*
256 * Now see if a default mount point is required
257 */
258 if (STREQ(mp2->m_name, "default")) {
259 if (ISSET(mp2->m_mask, DM_VOLNAME)) {
260 char nbuf[1024];
261 compute_automount_point(nbuf, dk->d_host, mp2->m_volname);
262 free(mp2->m_name);
263 mp2->m_name = strdup(nbuf);
264 log("%s:%s has default mount on %s", dk->d_host->h_hostname, dk->d_dev, mp2->m_name);
265 } else {
266 lerror(dk->d_ioloc, "no volname given for %s:%s", dk->d_host->h_hostname, dk->d_dev);
267 errors++;
268 }
269 }
270 /*
271 * Fill in the disk mount point
272 */
273 if (!errors && mp2 && mp2->m_name)
274 dk->d_mountpt = strdup(mp2->m_name);
275 else
276 dk->d_mountpt = strdup("error");
277
278 /*
279 * Analyze the mount tree
280 */
281 errors += analyze_dkmount_tree(q, 0, dk);
282
283 /*
284 * Analyze the export tree
285 */
286 errors += check_exportfs(q, 0);
287
288 return errors;
289}
290
291static void fixup_required_disk_info(dp)
292disk_fs *dp;
293{
294 /*
295 * "fstype"
296 */
297 if (ISSET(dp->d_mask, DF_FSTYPE)) {
298 if (STREQ(dp->d_fstype, "swap")) {
299 /*
300 * Fixup for a swap device
301 */
302 if (!ISSET(dp->d_mask, DF_PASSNO)) {
303 dp->d_passno = 0;
304 BITSET(dp->d_mask, DF_PASSNO);
305 } else if (dp->d_freq != 0) {
306 lwarning(dp->d_ioloc,
307 "Pass number for %s:%s is non-zero",
308 dp->d_host->h_hostname, dp->d_dev);
309 }
310
311 /*
312 * "freq"
313 */
314 if (!ISSET(dp->d_mask, DF_FREQ)) {
315 dp->d_freq = 0;
316 BITSET(dp->d_mask, DF_FREQ);
317 } else if (dp->d_freq != 0) {
318 lwarning(dp->d_ioloc,
319 "dump frequency for %s:%s is non-zero",
320 dp->d_host->h_hostname, dp->d_dev);
321 }
322
323 /*
324 * "opts"
325 */
326 if (!ISSET(dp->d_mask, DF_OPTS))
327 set_disk_fs(dp, DF_OPTS, strdup("swap"));
328
329 /*
330 * "mount"
331 */
332 if (!ISSET(dp->d_mask, DF_MOUNT)) {
333 qelem *q = new_que();
334 mount *m = new_mount();
335 m->m_name = strdup("swap");
336 m->m_mount = new_que();
337 ins_que(&m->m_q, q->q_back);
338 dp->d_mount = q;
339 BITSET(dp->d_mask, DF_MOUNT);
340 } else {
341 lerror(dp->d_ioloc, "%s: mount field specified for swap partition", dp->d_host->h_hostname);
342 }
343 } else if (STREQ(dp->d_fstype, "export")) {
344 /*
345 * "passno"
346 */
347 if (!ISSET(dp->d_mask, DF_PASSNO)) {
348 dp->d_passno = 0;
349 BITSET(dp->d_mask, DF_PASSNO);
350 } else if (dp->d_passno != 0) {
351 lwarning(dp->d_ioloc,
352 "pass number for %s:%s is non-zero",
353 dp->d_host->h_hostname, dp->d_dev);
354 }
355
356 /*
357 * "freq"
358 */
359 if (!ISSET(dp->d_mask, DF_FREQ)) {
360 dp->d_freq = 0;
361 BITSET(dp->d_mask, DF_FREQ);
362 } else if (dp->d_freq != 0) {
363 lwarning(dp->d_ioloc,
364 "dump frequency for %s:%s is non-zero",
365 dp->d_host->h_hostname, dp->d_dev);
366 }
367
368 /*
369 * "opts"
370 */
371 if (!ISSET(dp->d_mask, DF_OPTS))
372 set_disk_fs(dp, DF_OPTS, strdup("rw,defaults"));
373
374 }
375 }
376}
377
378static void fixup_required_mount_info(fp, de)
379fsmount *fp;
380dict_ent *de;
381{
382 if (!ISSET(fp->f_mask, FM_FROM)) {
383 if (de->de_count != 1) {
384 lerror(fp->f_ioloc, "ambiguous mount: %s is a replicated filesystem", fp->f_volname);
385 } else {
386 dict_data *dd;
387 mount *mp = 0;
388 ITER(dd, dict_data, &de->de_q) {
389 mp = (mount *) dd->dd_data;
390 break;
391 }
392 if (!mp)
393 abort();
394 fp->f_ref = mp;
395 set_fsmount(fp, FM_FROM, mp->m_dk->d_host->h_hostname);
396 log("set: %s comes from %s", fp->f_volname, fp->f_from);
397 }
398 }
399
400 if (!ISSET(fp->f_mask, FM_FSTYPE)) {
401 set_fsmount(fp, FM_FSTYPE, strdup("nfs"));
402 log("set: fstype is %s", fp->f_fstype);
403 }
404
405 if (!ISSET(fp->f_mask, FM_OPTS)) {
406 set_fsmount(fp, FM_OPTS, strdup("rw,nosuid,grpid,defaults"));
407 log("set: opts are %s", fp->f_opts);
408 }
409
410 if (!ISSET(fp->f_mask, FM_LOCALNAME)) {
411 if (fp->f_ref) {
412 set_fsmount(fp, FM_LOCALNAME, strdup(fp->f_volname));
413 log("set: localname is %s", fp->f_localname);
414 } else {
415 lerror(fp->f_ioloc, "cannot determine localname since volname %s is not uniquely defined", fp->f_volname);
416 }
417 }
418}
419
420/*
421 * For each disk on a host
422 * analyze the mount information
423 * and fill in any derivable
424 * details.
425 */
426static void analyze_drives(hp)
427host *hp;
428{
429 qelem *q = hp->h_disk_fs;
430 disk_fs *dp;
431
432 ITER(dp, disk_fs, q) {
433 int req;
434 log("Disk %s:", dp->d_dev);
435 dp->d_host = hp;
436 fixup_required_disk_info(dp);
437 req = ~dp->d_mask & DF_REQUIRED;
438 if (req)
439 show_required(dp->d_ioloc, req, dp->d_dev, hp->h_hostname, disk_fs_strings);
440 analyze_dkmounts(dp, dp->d_mount);
441 }
442}
443
444/*
445 * Check that all static mounts make sense and
446 * that the source volumes exist.
447 */
448static void analyze_mounts(hp)
449host *hp;
450{
451 qelem *q = hp->h_mount;
452 fsmount *fp;
453 int netbootp = 0;
454
455 ITER(fp, fsmount, q) {
456 char *p;
457 char *nn = strdup(fp->f_volname);
458 int req;
459 dict_ent *de;
460 int found = 0;
461 int matched = 0;
462 do {
463 p = 0;
464 de = find_volname(nn);
465 log("Mount: %s (trying %s)", fp->f_volname, nn);
466
467 if (de) {
468 found = 1;
469 /*
470 * Check that the from field is really exporting
471 * the filesystem requested.
472 */
473 if (ISSET(fp->f_mask, FM_FROM)) {
474 dict_data *dd;
475 mount *mp2 = 0;
476 ITER(dd, dict_data, &de->de_q) {
477 mount *mp = (mount *) dd->dd_data;
478 if (STREQ(mp->m_dk->d_host->h_hostname, fp->f_from)) {
479 mp2 = mp;
480 break;
481 }
482 }
483
484 if (mp2) {
485 fp->f_ref = mp2;
486 matched = 1;
487 break;
488 }
489 } else {
490 matched = 1;
491 break;
492 }
493 }
494 p = strrchr(nn, '/');
495 if (p)
496 *p = 0;
497 } while (de && p);
498 free(nn);
499
500 if (!found) {
501 lerror(fp->f_ioloc, "volname %s unknown", fp->f_volname);
502 } else if (matched) {
503 fixup_required_mount_info(fp, de);
504 req = ~fp->f_mask & FM_REQUIRED;
505 if (req) {
506 show_required(fp->f_ioloc, req, fp->f_volname, hp->h_hostname,
507 fsmount_strings);
508 } else if (strcmp(fp->f_localname, "/") == 0) {
509 hp->h_netroot = fp;
510 netbootp |= FM_NETROOT;
511 } else if (strcmp(fp->f_localname, "swap") == 0) {
512 hp->h_netswap = fp;
513 netbootp |= FM_NETSWAP;
514 }
515 } else {
516 lerror(fp->f_ioloc, "volname %s not exported from %s", fp->f_volname,
517 fp->f_from ? fp->f_from : "anywhere");
518 }
519 }
520
521 if (netbootp && (netbootp != FM_NETBOOT))
522 lerror(hp->h_ioloc, "network booting requires both root and swap areas");
523}
524
525void analyze_hosts(q)
526qelem *q;
527{
528 host *hp;
529
530 show_area_being_processed("analyze hosts", 5);
531
532 /*
533 * Check all drives
534 */
535 ITER(hp, host, q) {
536 log("disks on host %s", hp->h_hostname);
537 show_new("ana-host");
538 hp->h_hostpath = compute_hostpath(hp->h_hostname);
539
540 if (hp->h_disk_fs)
541 analyze_drives(hp);
542
543 }
544
545 show_area_being_processed("analyze mounts", 5);
546
547 /*
548 * Check static mounts
549 */
550 ITER(hp, host, q) {
551 log("mounts on host %s", hp->h_hostname);
552 show_new("ana-mount");
553 if (hp->h_mount)
554 analyze_mounts(hp);
555
556 }
557}
558
559/*
560 * Check an automount request
561 */
562static void analyze_automount(ap)
563automount *ap;
564{
565 dict_ent *de = find_volname(ap->a_volname);
566 if (de) {
567 ap->a_mounted = de;
568 } else {
569 if (STREQ(ap->a_volname, ap->a_name))
570 lerror(ap->a_ioloc, "unknown volname %s automounted", ap->a_volname);
571 else
572 lerror(ap->a_ioloc, "unknown volname %s automounted on %s", ap->a_volname, ap->a_name);
573 }
574}
575
576static void analyze_automount_tree(q, pref, lvl)
577qelem *q;
578char *pref;
579int lvl;
580{
581 automount *ap;
582
583 ITER(ap, automount, q) {
584 char nname[1024];
585 if (lvl > 0 || ap->a_mount)
586 if (ap->a_name[1] && strchr(ap->a_name+1, '/'))
587 lerror(ap->a_ioloc, "not allowed '/' in a directory name");
588 sprintf(nname, "%s/%s", pref, ap->a_name);
589 free(ap->a_name);
590 ap->a_name = strdup(nname[1] == '/' ? nname+1 : nname);
591 log("automount point %s:", ap->a_name);
592 show_new("ana-automount");
593 if (ap->a_mount) {
594 analyze_automount_tree(ap->a_mount, ap->a_name, lvl+1);
595 } else if (ap->a_volname) {
596 log("\tautomount from %s", ap->a_volname);
597 analyze_automount(ap);
598 } else if (ap->a_symlink) {
599 log("\tsymlink to %s", ap->a_symlink);
600 } else {
601 ap->a_volname = strdup(ap->a_name);
602 log("\timplicit automount from %s", ap->a_volname);
603 analyze_automount(ap);
604 }
605 }
606}
607
608void analyze_automounts(q)
609qelem *q;
610{
611 auto_tree *tp;
612
613 show_area_being_processed("analyze automount", 5);
614 /*
615 * q is a list of automounts
616 */
617 ITER(tp, auto_tree, q)
618 analyze_automount_tree(tp->t_mount, "", 0);
619}