2 * Copyright (c) 1997-2014 Erez Zadok
3 * Copyright (c) 1989 Jan-Simon Pendry
4 * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
5 * Copyright (c) 1989 The Regents of the University of California.
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry at Imperial College, London.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * File: am-utils/fsinfo/fsi_analyze.c
41 * Analyze filesystem declarations
43 * Note: most of this is magic!
48 #endif /* HAVE_CONFIG_H */
53 char *disk_fs_strings[] =
55 "fstype", "opts", "dumpset", "passno", "freq", "mount", "log", NULL,
58 char *mount_strings[] =
60 "volname", "exportfs", NULL,
63 char *fsmount_strings[] =
65 "as", "volname", "fstype", "opts", "from", NULL,
68 char *host_strings[] =
70 "host", "netif", "config", "arch", "cluster", "os", NULL,
73 char *ether_if_strings[] =
75 "inaddr", "netmask", "hwaddr", NULL,
80 * Strip off the trailing part of a domain
81 * to produce a short-form domain relative
82 * to the local host domain.
83 * Note that this has no effect if the domain
84 * names do not have the same number of
85 * components. If that restriction proves
86 * to be a problem then the loop needs recoding
87 * to skip from right to left and do partial
88 * matches along the way -- ie more expensive.
91 domain_strip(char *otherdom, char *localdom)
95 if ((p1 = strchr(otherdom, '.')) &&
96 (p2 = strchr(localdom, '.')) &&
97 STREQ(p1 + 1, p2 + 1))
103 * Take a little-endian domain name and
104 * transform into a big-endian Un*x pathname.
105 * For example: kiska.doc.ic -> ic/doc/kiska
108 compute_hostpath(char *hn)
110 char *p = xmalloc(MAXPATHLEN);
112 char path[MAXPATHLEN];
114 xstrlcpy(p, hn, MAXPATHLEN);
115 domain_strip(p, hostname);
122 xstrlcat(path, d + 1, sizeof(path));
123 xstrlcat(path, "/", sizeof(path));
125 xstrlcat(path, p, sizeof(path));
129 fsi_log("hostpath of '%s' is '%s'", hn, path);
131 xstrlcpy(p, path, MAXPATHLEN);
137 find_volname(char *nn)
140 char *p = xstrdup(nn);
144 fsi_log("Searching for volname %s", p);
145 de = dict_locate(dict_of_volnames, p);
157 show_required(ioloc *l, int mask, char *info, char *hostname, char *strings[])
160 fsi_log("mask left for %s:%s is %#x", hostname, info, mask);
162 for (i = 0; strings[i]; i++)
164 lerror(l, "%s:%s needs field \"%s\"", hostname, info, strings[i]);
169 * Check and fill in "exportfs" details.
170 * Make sure the m_exported field references
171 * the most local node with an "exportfs" entry.
174 check_exportfs(qelem *q, fsi_mount *e)
179 ITER(mp, fsi_mount, q) {
180 if (ISSET(mp->m_mask, DM_EXPORTFS)) {
182 lwarning(mp->m_ioloc, "%s has duplicate exportfs data", mp->m_name);
184 if (!ISSET(mp->m_mask, DM_VOLNAME))
185 set_mount(mp, DM_VOLNAME, xstrdup(mp->m_name));
191 * Recursively descend the mount tree
194 errors += check_exportfs(mp->m_mount, mp->m_exported);
197 * If a volume name has been specified, but this node and none
198 * of its parents has been exported, report an error.
200 if (ISSET(mp->m_mask, DM_VOLNAME) && !mp->m_exported) {
201 lerror(mp->m_ioloc, "%s has a volname but no exportfs data", mp->m_name);
211 analyze_dkmount_tree(qelem *q, fsi_mount *parent, disk_fs *dk)
216 ITER(mp, fsi_mount, q) {
217 fsi_log("Mount %s:", mp->m_name);
220 xsnprintf(n, sizeof(n), "%s/%s", parent->m_name, mp->m_name);
221 if (*mp->m_name == '/')
222 lerror(mp->m_ioloc, "sub-directory %s of %s starts with '/'", mp->m_name, parent->m_name);
223 else if (STREQ(mp->m_name, "default"))
224 lwarning(mp->m_ioloc, "sub-directory of %s is named \"default\"", parent->m_name);
225 fsi_log("Changing name %s to %s", mp->m_name, n);
227 mp->m_name = xstrdup(n);
230 mp->m_name_len = strlen(mp->m_name);
231 mp->m_parent = parent;
234 analyze_dkmount_tree(mp->m_mount, mp, dk);
242 * The mount tree is a singleton list
243 * containing the top-level mount
247 analyze_dkmounts(disk_fs *dk, qelem *q)
250 fsi_mount *mp, *mp2 = NULL;
254 * First scan the list of subdirs to make
255 * sure there is only one - and remember it
258 ITER(mp, fsi_mount, q) {
268 lerror(dk->d_ioloc, "%s:%s has no mount point", dk->d_host->h_hostname, dk->d_dev);
273 lerror(dk->d_ioloc, "%s:%s has more than one mount point", dk->d_host->h_hostname, dk->d_dev);
278 * Now see if a default mount point is required
280 if (mp2 && STREQ(mp2->m_name, "default")) {
281 if (ISSET(mp2->m_mask, DM_VOLNAME)) {
283 compute_automount_point(nbuf, sizeof(nbuf), dk->d_host, mp2->m_volname);
285 mp2->m_name = xstrdup(nbuf);
286 fsi_log("%s:%s has default mount on %s", dk->d_host->h_hostname, dk->d_dev, mp2->m_name);
288 lerror(dk->d_ioloc, "no volname given for %s:%s", dk->d_host->h_hostname, dk->d_dev);
294 * Fill in the disk mount point
296 if (!errors && mp2 && mp2->m_name)
297 dk->d_mountpt = xstrdup(mp2->m_name);
299 dk->d_mountpt = xstrdup("error");
302 * Analyze the mount tree
304 errors += analyze_dkmount_tree(q, NULL, dk);
307 * Analyze the export tree
309 errors += check_exportfs(q, NULL);
316 fixup_required_disk_info(disk_fs *dp)
321 if (ISSET(dp->d_mask, DF_FSTYPE)) {
322 if (STREQ(dp->d_fstype, "swap")) {
325 * Fixup for a swap device
327 if (!ISSET(dp->d_mask, DF_PASSNO)) {
329 BITSET(dp->d_mask, DF_PASSNO);
330 } else if (dp->d_freq != 0) {
331 lwarning(dp->d_ioloc,
332 "Pass number for %s:%s is non-zero",
333 dp->d_host->h_hostname, dp->d_dev);
339 if (!ISSET(dp->d_mask, DF_FREQ)) {
341 BITSET(dp->d_mask, DF_FREQ);
342 } else if (dp->d_freq != 0) {
343 lwarning(dp->d_ioloc,
344 "dump frequency for %s:%s is non-zero",
345 dp->d_host->h_hostname, dp->d_dev);
351 if (!ISSET(dp->d_mask, DF_OPTS))
352 set_disk_fs(dp, DF_OPTS, xstrdup("swap"));
357 if (!ISSET(dp->d_mask, DF_MOUNT)) {
358 qelem *q = new_que();
359 fsi_mount *m = new_mount();
361 m->m_name = xstrdup("swap");
362 m->m_mount = new_que();
363 ins_que(&m->m_q, q->q_back);
365 BITSET(dp->d_mask, DF_MOUNT);
367 lerror(dp->d_ioloc, "%s: mount field specified for swap partition", dp->d_host->h_hostname);
369 } else if (STREQ(dp->d_fstype, "export")) {
374 if (!ISSET(dp->d_mask, DF_PASSNO)) {
376 BITSET(dp->d_mask, DF_PASSNO);
377 } else if (dp->d_passno != 0) {
378 lwarning(dp->d_ioloc,
379 "pass number for %s:%s is non-zero",
380 dp->d_host->h_hostname, dp->d_dev);
386 if (!ISSET(dp->d_mask, DF_FREQ)) {
388 BITSET(dp->d_mask, DF_FREQ);
389 } else if (dp->d_freq != 0) {
390 lwarning(dp->d_ioloc,
391 "dump frequency for %s:%s is non-zero",
392 dp->d_host->h_hostname, dp->d_dev);
398 if (!ISSET(dp->d_mask, DF_OPTS))
399 set_disk_fs(dp, DF_OPTS, xstrdup("rw,defaults"));
407 fixup_required_mount_info(fsmount *fp, dict_ent *de)
409 if (!ISSET(fp->f_mask, FM_FROM)) {
410 if (de->de_count != 1) {
411 lerror(fp->f_ioloc, "ambiguous mount: %s is a replicated filesystem", fp->f_volname);
414 fsi_mount *mp = NULL;
415 dd = AM_FIRST(dict_data, &de->de_q);
416 mp = (fsi_mount *) dd->dd_data;
420 set_fsmount(fp, FM_FROM, mp->m_dk->d_host->h_hostname);
421 fsi_log("set: %s comes from %s", fp->f_volname, fp->f_from);
425 if (!ISSET(fp->f_mask, FM_FSTYPE)) {
426 set_fsmount(fp, FM_FSTYPE, xstrdup("nfs"));
427 fsi_log("set: fstype is %s", fp->f_fstype);
430 if (!ISSET(fp->f_mask, FM_OPTS)) {
431 set_fsmount(fp, FM_OPTS, xstrdup("rw,nosuid,grpid,defaults"));
432 fsi_log("set: opts are %s", fp->f_opts);
435 if (!ISSET(fp->f_mask, FM_LOCALNAME)) {
437 set_fsmount(fp, FM_LOCALNAME, xstrdup(fp->f_volname));
438 fsi_log("set: localname is %s", fp->f_localname);
440 lerror(fp->f_ioloc, "cannot determine localname since volname %s is not uniquely defined", fp->f_volname);
447 * For each disk on a host
448 * analyze the mount information
449 * and fill in any derivable
453 analyze_drives(host *hp)
455 qelem *q = hp->h_disk_fs;
458 ITER(dp, disk_fs, q) {
460 fsi_log("Disk %s:", dp->d_dev);
462 fixup_required_disk_info(dp);
463 req = ~dp->d_mask & DF_REQUIRED;
465 show_required(dp->d_ioloc, req, dp->d_dev, hp->h_hostname, disk_fs_strings);
466 analyze_dkmounts(dp, dp->d_mount);
472 * Check that all static mounts make sense and
473 * that the source volumes exist.
476 analyze_mounts(host *hp)
478 qelem *q = hp->h_mount;
482 ITER(fp, fsmount, q) {
484 char *nn = xstrdup(fp->f_volname);
486 dict_ent *de = (dict_ent *) NULL;
490 if (ISSET(fp->f_mask, FM_DIRECT)) {
496 de = find_volname(nn);
497 fsi_log("Mount: %s (trying %s)", fp->f_volname, nn);
503 * Check that the from field is really exporting
504 * the filesystem requested.
505 * LBL: If fake mount, then don't care about
508 if (ISSET(fp->f_mask, FM_FROM) && !ISSET(fp->f_mask, FM_DIRECT)) {
510 fsi_mount *mp2 = NULL;
512 ITER(dd, dict_data, &de->de_q) {
513 fsi_mount *mp = (fsi_mount *) dd->dd_data;
516 STREQ(mp->m_dk->d_host->h_hostname, fp->f_from)) {
532 p = strrchr(nn, '/');
539 lerror(fp->f_ioloc, "volname %s unknown", fp->f_volname);
540 } else if (matched) {
543 fixup_required_mount_info(fp, de);
544 req = ~fp->f_mask & FM_REQUIRED;
546 show_required(fp->f_ioloc, req, fp->f_volname, hp->h_hostname,
548 } else if (STREQ(fp->f_localname, "/")) {
550 netbootp |= FM_NETROOT;
551 } else if (STREQ(fp->f_localname, "swap")) {
553 netbootp |= FM_NETSWAP;
557 lerror(fp->f_ioloc, "volname %s not exported from %s", fp->f_volname,
558 fp->f_from ? fp->f_from : "anywhere");
562 if (netbootp && (netbootp != FM_NETBOOT))
563 lerror(hp->h_ioloc, "network booting requires both root and swap areas");
568 analyze_hosts(qelem *q)
572 show_area_being_processed("analyze hosts", 5);
578 fsi_log("disks on host %s", hp->h_hostname);
579 show_new("ana-host");
580 hp->h_hostpath = compute_hostpath(hp->h_hostname);
587 show_area_being_processed("analyze mounts", 5);
590 * Check static mounts
593 fsi_log("mounts on host %s", hp->h_hostname);
594 show_new("ana-mount");
603 * Check an automount request
606 analyze_automount(automount *ap)
608 dict_ent *de = find_volname(ap->a_volname);
613 if (STREQ(ap->a_volname, ap->a_name))
614 lerror(ap->a_ioloc, "unknown volname %s automounted", ap->a_volname);
616 lerror(ap->a_ioloc, "unknown volname %s automounted on %s", ap->a_volname, ap->a_name);
622 analyze_automount_tree(qelem *q, char *pref, int lvl)
626 ITER(ap, automount, q) {
629 if (lvl > 0 || ap->a_mount)
630 if (ap->a_name[1] && strchr(ap->a_name + 1, '/'))
631 lerror(ap->a_ioloc, "not allowed '/' in a directory name");
632 xsnprintf(nname, sizeof(nname), "%s/%s", pref, ap->a_name);
634 ap->a_name = xstrdup(nname[1] == '/' ? nname + 1 : nname);
635 fsi_log("automount point %s:", ap->a_name);
636 show_new("ana-automount");
639 analyze_automount_tree(ap->a_mount, ap->a_name, lvl + 1);
640 } else if (ap->a_hardwiredfs) {
641 fsi_log("\thardwired from %s to %s", ap->a_volname, ap->a_hardwiredfs);
642 } else if (ap->a_volname) {
643 fsi_log("\tautomount from %s", ap->a_volname);
644 analyze_automount(ap);
645 } else if (ap->a_symlink) {
646 fsi_log("\tsymlink to %s", ap->a_symlink);
648 ap->a_volname = xstrdup(ap->a_name);
649 fsi_log("\timplicit automount from %s", ap->a_volname);
650 analyze_automount(ap);
657 analyze_automounts(qelem *q)
661 show_area_being_processed("analyze automount", 5);
664 * q is a list of automounts
666 ITER(tp, auto_tree, q)
667 analyze_automount_tree(tp->t_mount, "", 0);