diff --git a/lib/lib80211/lib80211_regdomain.c b/lib/lib80211/lib80211_regdomain.c index f5ed236467f5..189d4661c78b 100644 --- a/lib/lib80211/lib80211_regdomain.c +++ b/lib/lib80211/lib80211_regdomain.c @@ -1,740 +1,737 @@ /*- * Copyright (c) 2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = "$FreeBSD$"; -#endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib80211_regdomain.h" #include #define MAXLEVEL 20 struct mystate { XML_Parser parser; struct regdata *rdp; struct regdomain *rd; /* current domain */ struct netband *netband; /* current netband */ struct freqband *freqband; /* current freqband */ struct country *country; /* current country */ netband_head *curband; /* current netband list */ int level; struct sbuf *sbuf[MAXLEVEL]; int nident; }; struct ident { const void *id; void *p; enum { DOMAIN, COUNTRY, FREQBAND } type; }; static void start_element(void *data, const char *name, const char **attr) { #define iseq(a,b) (strcasecmp(a,b) == 0) struct mystate *mt; const void *id, *ref, *mode; int i; mt = data; if (++mt->level == MAXLEVEL) { /* XXX force parser to abort */ return; } mt->sbuf[mt->level] = sbuf_new_auto(); id = ref = mode = NULL; for (i = 0; attr[i] != NULL; i += 2) { if (iseq(attr[i], "id")) { id = attr[i+1]; } else if (iseq(attr[i], "ref")) { ref = attr[i+1]; } else if (iseq(attr[i], "mode")) { mode = attr[i+1]; } else printf("%*.*s[%s = %s]\n", mt->level + 1, mt->level + 1, "", attr[i], attr[i+1]); } if (iseq(name, "rd") && mt->rd == NULL) { if (mt->country == NULL) { mt->rd = calloc(1, sizeof(struct regdomain)); mt->rd->name = strdup(id); mt->nident++; LIST_INSERT_HEAD(&mt->rdp->domains, mt->rd, next); } else mt->country->rd = (void *)strdup(ref); return; } if (iseq(name, "defcc") && mt->rd != NULL) { mt->rd->cc = (void *)strdup(ref); return; } if (iseq(name, "netband") && mt->curband == NULL && mt->rd != NULL) { if (mode == NULL) { warnx("no mode for netband at line %ld", XML_GetCurrentLineNumber(mt->parser)); return; } if (iseq(mode, "11b")) mt->curband = &mt->rd->bands_11b; else if (iseq(mode, "11g")) mt->curband = &mt->rd->bands_11g; else if (iseq(mode, "11a")) mt->curband = &mt->rd->bands_11a; else if (iseq(mode, "11ng")) mt->curband = &mt->rd->bands_11ng; else if (iseq(mode, "11na")) mt->curband = &mt->rd->bands_11na; else if (iseq(mode, "11ac")) mt->curband = &mt->rd->bands_11ac; else if (iseq(mode, "11acg")) mt->curband = &mt->rd->bands_11acg; else warnx("unknown mode \"%s\" at line %ld", __DECONST(char *, mode), XML_GetCurrentLineNumber(mt->parser)); return; } if (iseq(name, "band") && mt->netband == NULL) { if (mt->curband == NULL) { warnx("band without enclosing netband at line %ld", XML_GetCurrentLineNumber(mt->parser)); return; } mt->netband = calloc(1, sizeof(struct netband)); LIST_INSERT_HEAD(mt->curband, mt->netband, next); return; } if (iseq(name, "freqband") && mt->freqband == NULL && mt->netband != NULL) { /* XXX handle inlines and merge into table? */ if (mt->netband->band != NULL) { warnx("duplicate freqband at line %ld ignored", XML_GetCurrentLineNumber(mt->parser)); /* XXX complain */ } else mt->netband->band = (void *)strdup(ref); return; } if (iseq(name, "country") && mt->country == NULL) { mt->country = calloc(1, sizeof(struct country)); mt->country->isoname = strdup(id); mt->country->code = NO_COUNTRY; mt->nident++; LIST_INSERT_HEAD(&mt->rdp->countries, mt->country, next); return; } if (iseq(name, "freqband") && mt->freqband == NULL) { mt->freqband = calloc(1, sizeof(struct freqband)); mt->freqband->id = strdup(id); mt->nident++; LIST_INSERT_HEAD(&mt->rdp->freqbands, mt->freqband, next); return; } #undef iseq } static int decode_flag(struct mystate *mt, const char *p, int len) { #define iseq(a,b) (strcasecmp(a,b) == 0) static const struct { const char *name; int len; uint32_t value; } flags[] = { #define FLAG(x) { #x, sizeof(#x)-1, x } FLAG(IEEE80211_CHAN_A), FLAG(IEEE80211_CHAN_B), FLAG(IEEE80211_CHAN_G), FLAG(IEEE80211_CHAN_HT20), FLAG(IEEE80211_CHAN_HT40), FLAG(IEEE80211_CHAN_VHT20), FLAG(IEEE80211_CHAN_VHT40), FLAG(IEEE80211_CHAN_VHT80), FLAG(IEEE80211_CHAN_VHT160), /* * XXX VHT80P80? This likely should be done by * 80MHz chan logic in net80211 / ifconfig. */ FLAG(IEEE80211_CHAN_ST), FLAG(IEEE80211_CHAN_TURBO), FLAG(IEEE80211_CHAN_PASSIVE), FLAG(IEEE80211_CHAN_DFS), FLAG(IEEE80211_CHAN_CCK), FLAG(IEEE80211_CHAN_OFDM), FLAG(IEEE80211_CHAN_2GHZ), FLAG(IEEE80211_CHAN_5GHZ), FLAG(IEEE80211_CHAN_DYN), FLAG(IEEE80211_CHAN_GFSK), FLAG(IEEE80211_CHAN_GSM), FLAG(IEEE80211_CHAN_STURBO), FLAG(IEEE80211_CHAN_HALF), FLAG(IEEE80211_CHAN_QUARTER), FLAG(IEEE80211_CHAN_HT40U), FLAG(IEEE80211_CHAN_HT40D), FLAG(IEEE80211_CHAN_4MSXMIT), FLAG(IEEE80211_CHAN_NOADHOC), FLAG(IEEE80211_CHAN_NOHOSTAP), FLAG(IEEE80211_CHAN_11D), FLAG(IEEE80211_CHAN_FHSS), FLAG(IEEE80211_CHAN_PUREG), FLAG(IEEE80211_CHAN_108A), FLAG(IEEE80211_CHAN_108G), #undef FLAG { "ECM", 3, REQ_ECM }, { "INDOOR", 6, REQ_INDOOR }, { "OUTDOOR", 7, REQ_OUTDOOR }, }; unsigned int i; for (i = 0; i < nitems(flags); i++) if (len == flags[i].len && iseq(p, flags[i].name)) return flags[i].value; warnx("unknown flag \"%.*s\" at line %ld ignored", len, p, XML_GetCurrentLineNumber(mt->parser)); return 0; #undef iseq } static void end_element(void *data, const char *name) { #define iseq(a,b) (strcasecmp(a,b) == 0) struct mystate *mt; int len; char *p; mt = data; sbuf_finish(mt->sbuf[mt->level]); p = sbuf_data(mt->sbuf[mt->level]); len = sbuf_len(mt->sbuf[mt->level]); /* ... */ if (iseq(name, "freqstart") && mt->freqband != NULL) { mt->freqband->freqStart = strtoul(p, NULL, 0); goto done; } if (iseq(name, "freqend") && mt->freqband != NULL) { mt->freqband->freqEnd = strtoul(p, NULL, 0); goto done; } if (iseq(name, "chanwidth") && mt->freqband != NULL) { mt->freqband->chanWidth = strtoul(p, NULL, 0); goto done; } if (iseq(name, "chansep") && mt->freqband != NULL) { mt->freqband->chanSep = strtoul(p, NULL, 0); goto done; } if (iseq(name, "flags")) { if (mt->freqband != NULL) mt->freqband->flags |= decode_flag(mt, p, len); else if (mt->netband != NULL) mt->netband->flags |= decode_flag(mt, p, len); else { warnx("flags without freqband or netband at line %ld ignored", XML_GetCurrentLineNumber(mt->parser)); } goto done; } /* ... */ if (iseq(name, "name") && mt->rd != NULL) { mt->rd->name = strdup(p); goto done; } if (iseq(name, "sku") && mt->rd != NULL) { mt->rd->sku = strtoul(p, NULL, 0); goto done; } if (iseq(name, "netband") && mt->rd != NULL) { mt->curband = NULL; goto done; } /* ... */ if (iseq(name, "freqband") && mt->netband != NULL) { /* XXX handle inline freqbands */ goto done; } if (iseq(name, "maxpower") && mt->netband != NULL) { mt->netband->maxPower = strtoul(p, NULL, 0); goto done; } if (iseq(name, "maxpowerdfs") && mt->netband != NULL) { mt->netband->maxPowerDFS = strtoul(p, NULL, 0); goto done; } if (iseq(name, "maxantgain") && mt->netband != NULL) { mt->netband->maxAntGain = strtoul(p, NULL, 0); goto done; } /* ... */ if (iseq(name, "isocc") && mt->country != NULL) { mt->country->code = strtoul(p, NULL, 0); goto done; } if (iseq(name, "name") && mt->country != NULL) { mt->country->name = strdup(p); goto done; } if (len != 0) { warnx("unexpected XML token \"%s\" data \"%s\" at line %ld", name, p, XML_GetCurrentLineNumber(mt->parser)); /* XXX goto done? */ } /* */ if (iseq(name, "freqband") && mt->freqband != NULL) { /* XXX must have start/end frequencies */ /* XXX must have channel width/sep */ mt->freqband = NULL; goto done; } /* */ if (iseq(name, "rd") && mt->rd != NULL) { mt->rd = NULL; goto done; } /* */ if (iseq(name, "band") && mt->netband != NULL) { if (mt->netband->band == NULL) { warnx("no freqbands for band at line %ld", XML_GetCurrentLineNumber(mt->parser)); } if (mt->netband->maxPower == 0) { warnx("no maxpower for band at line %ld", XML_GetCurrentLineNumber(mt->parser)); } /* default max power w/ DFS to max power */ if (mt->netband->maxPowerDFS == 0) mt->netband->maxPowerDFS = mt->netband->maxPower; mt->netband = NULL; goto done; } /* */ if (iseq(name, "netband") && mt->netband != NULL) { mt->curband = NULL; goto done; } /* */ if (iseq(name, "country") && mt->country != NULL) { /* XXX NO_COUNTRY should be in the net80211 country enum */ if ((int) mt->country->code == NO_COUNTRY) { warnx("no ISO cc for country at line %ld", XML_GetCurrentLineNumber(mt->parser)); } if (mt->country->name == NULL) { warnx("no name for country at line %ld", XML_GetCurrentLineNumber(mt->parser)); } if (mt->country->rd == NULL) { warnx("no regdomain reference for country at line %ld", XML_GetCurrentLineNumber(mt->parser)); } mt->country = NULL; goto done; } done: sbuf_delete(mt->sbuf[mt->level]); mt->sbuf[mt->level--] = NULL; #undef iseq } static void char_data(void *data, const XML_Char *s, int len) { struct mystate *mt; const char *b, *e; mt = data; b = s; e = s + len-1; for (; isspace(*b) && b < e; b++) ; for (; isspace(*e) && e > b; e++) ; if (e != b || (*b != '\0' && !isspace(*b))) sbuf_bcat(mt->sbuf[mt->level], b, e-b+1); } static void * findid(struct regdata *rdp, const void *id, int type) { struct ident *ip; for (ip = rdp->ident; ip->id != NULL; ip++) if ((int) ip->type == type && strcasecmp(ip->id, id) == 0) return ip->p; return NULL; } /* * Parse an regdomain XML configuration and build the internal representation. */ int lib80211_regdomain_readconfig(struct regdata *rdp, const void *p, size_t len) { struct mystate *mt; struct regdomain *dp; struct country *cp; struct freqband *fp; struct netband *nb; const void *id; int i, errors; memset(rdp, 0, sizeof(struct regdata)); mt = calloc(1, sizeof(struct mystate)); if (mt == NULL) return ENOMEM; /* parse the XML input */ mt->rdp = rdp; mt->parser = XML_ParserCreate(NULL); XML_SetUserData(mt->parser, mt); XML_SetElementHandler(mt->parser, start_element, end_element); XML_SetCharacterDataHandler(mt->parser, char_data); if (XML_Parse(mt->parser, p, len, 1) != XML_STATUS_OK) { warnx("%s: %s at line %ld", __func__, XML_ErrorString(XML_GetErrorCode(mt->parser)), XML_GetCurrentLineNumber(mt->parser)); return -1; } XML_ParserFree(mt->parser); /* setup the identifer table */ rdp->ident = calloc(sizeof(struct ident), mt->nident + 1); if (rdp->ident == NULL) return ENOMEM; free(mt); errors = 0; i = 0; LIST_FOREACH(dp, &rdp->domains, next) { rdp->ident[i].id = dp->name; rdp->ident[i].p = dp; rdp->ident[i].type = DOMAIN; i++; } LIST_FOREACH(fp, &rdp->freqbands, next) { rdp->ident[i].id = fp->id; rdp->ident[i].p = fp; rdp->ident[i].type = FREQBAND; i++; } LIST_FOREACH(cp, &rdp->countries, next) { rdp->ident[i].id = cp->isoname; rdp->ident[i].p = cp; rdp->ident[i].type = COUNTRY; i++; } /* patch references */ LIST_FOREACH(dp, &rdp->domains, next) { if (dp->cc != NULL) { id = dp->cc; dp->cc = findid(rdp, id, COUNTRY); if (dp->cc == NULL) { warnx("undefined country \"%s\"", __DECONST(char *, id)); errors++; } free(__DECONST(char *, id)); } LIST_FOREACH(nb, &dp->bands_11b, next) { id = findid(rdp, nb->band, FREQBAND); if (id == NULL) { warnx("undefined 11b band \"%s\"", __DECONST(char *, nb->band)); errors++; } nb->band = id; } LIST_FOREACH(nb, &dp->bands_11g, next) { id = findid(rdp, nb->band, FREQBAND); if (id == NULL) { warnx("undefined 11g band \"%s\"", __DECONST(char *, nb->band)); errors++; } nb->band = id; } LIST_FOREACH(nb, &dp->bands_11a, next) { id = findid(rdp, nb->band, FREQBAND); if (id == NULL) { warnx("undefined 11a band \"%s\"", __DECONST(char *, nb->band)); errors++; } nb->band = id; } LIST_FOREACH(nb, &dp->bands_11ng, next) { id = findid(rdp, nb->band, FREQBAND); if (id == NULL) { warnx("undefined 11ng band \"%s\"", __DECONST(char *, nb->band)); errors++; } nb->band = id; } LIST_FOREACH(nb, &dp->bands_11na, next) { id = findid(rdp, nb->band, FREQBAND); if (id == NULL) { warnx("undefined 11na band \"%s\"", __DECONST(char *, nb->band)); errors++; } nb->band = id; } LIST_FOREACH(nb, &dp->bands_11ac, next) { id = findid(rdp, nb->band, FREQBAND); if (id == NULL) { warnx("undefined 11ac band \"%s\"", __DECONST(char *, nb->band)); errors++; } nb->band = id; } LIST_FOREACH(nb, &dp->bands_11acg, next) { id = findid(rdp, nb->band, FREQBAND); if (id == NULL) { warnx("undefined 11acg band \"%s\"", __DECONST(char *, nb->band)); errors++; } nb->band = id; } } LIST_FOREACH(cp, &rdp->countries, next) { id = cp->rd; cp->rd = findid(rdp, id, DOMAIN); if (cp->rd == NULL) { warnx("undefined country \"%s\"", __DECONST(char *, id)); errors++; } free(__DECONST(char *, id)); } return errors ? EINVAL : 0; } static void cleanup_bands(netband_head *head) { struct netband *nb; for (;;) { nb = LIST_FIRST(head); if (nb == NULL) break; LIST_REMOVE(nb, next); free(nb); } } /* * Cleanup state/resources for a previously parsed regdomain database. */ void lib80211_regdomain_cleanup(struct regdata *rdp) { free(rdp->ident); rdp->ident = NULL; for (;;) { struct regdomain *dp = LIST_FIRST(&rdp->domains); if (dp == NULL) break; LIST_REMOVE(dp, next); cleanup_bands(&dp->bands_11b); cleanup_bands(&dp->bands_11g); cleanup_bands(&dp->bands_11a); cleanup_bands(&dp->bands_11ng); cleanup_bands(&dp->bands_11na); cleanup_bands(&dp->bands_11ac); cleanup_bands(&dp->bands_11acg); if (dp->name != NULL) free(__DECONST(char *, dp->name)); } for (;;) { struct country *cp = LIST_FIRST(&rdp->countries); if (cp == NULL) break; LIST_REMOVE(cp, next); if (cp->name != NULL) free(__DECONST(char *, cp->name)); free(cp); } for (;;) { struct freqband *fp = LIST_FIRST(&rdp->freqbands); if (fp == NULL) break; LIST_REMOVE(fp, next); free(fp); } } struct regdata * lib80211_alloc_regdata(void) { struct regdata *rdp; struct stat sb; void *xml; int fd; rdp = calloc(1, sizeof(struct regdata)); fd = open(_PATH_REGDOMAIN, O_RDONLY); if (fd < 0) { #ifdef DEBUG warn("%s: open(%s)", __func__, _PATH_REGDOMAIN); #endif free(rdp); return NULL; } if (fstat(fd, &sb) < 0) { #ifdef DEBUG warn("%s: fstat(%s)", __func__, _PATH_REGDOMAIN); #endif close(fd); free(rdp); return NULL; } xml = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (xml == MAP_FAILED) { #ifdef DEBUG warn("%s: mmap", __func__); #endif close(fd); free(rdp); return NULL; } if (lib80211_regdomain_readconfig(rdp, xml, sb.st_size) != 0) { #ifdef DEBUG warn("%s: error reading regulatory database", __func__); #endif munmap(xml, sb.st_size); close(fd); free(rdp); return NULL; } munmap(xml, sb.st_size); close(fd); return rdp; } void lib80211_free_regdata(struct regdata *rdp) { lib80211_regdomain_cleanup(rdp); free(rdp); } /* * Lookup a regdomain by SKU. */ const struct regdomain * lib80211_regdomain_findbysku(const struct regdata *rdp, enum RegdomainCode sku) { const struct regdomain *dp; LIST_FOREACH(dp, &rdp->domains, next) { if (dp->sku == sku) return dp; } return NULL; } /* * Lookup a regdomain by name. */ const struct regdomain * lib80211_regdomain_findbyname(const struct regdata *rdp, const char *name) { const struct regdomain *dp; LIST_FOREACH(dp, &rdp->domains, next) { if (strcasecmp(dp->name, name) == 0) return dp; } return NULL; } /* * Lookup a country by ISO country code. */ const struct country * lib80211_country_findbycc(const struct regdata *rdp, enum ISOCountryCode cc) { const struct country *cp; LIST_FOREACH(cp, &rdp->countries, next) { if (cp->code == cc) return cp; } return NULL; } /* * Lookup a country by ISO/long name. */ const struct country * lib80211_country_findbyname(const struct regdata *rdp, const char *name) { const struct country *cp; int len; len = strlen(name); LIST_FOREACH(cp, &rdp->countries, next) { if (strcasecmp(cp->isoname, name) == 0) return cp; } LIST_FOREACH(cp, &rdp->countries, next) { if (strncasecmp(cp->name, name, len) == 0) return cp; } return NULL; } diff --git a/lib/libc/net/nslexer.l b/lib/libc/net/nslexer.l index bd3c02dcacf4..ce0f18670e21 100644 --- a/lib/libc/net/nslexer.l +++ b/lib/libc/net/nslexer.l @@ -1,114 +1,108 @@ %{ /* $NetBSD: nslexer.l,v 1.3 1999/01/25 00:16:17 lukem Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 1997, 1998, 1999 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Luke Mewburn. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ -#include -#if defined(LIBC_SCCS) && !defined(lint) -static char *rcsid = - "$FreeBSD$"; -#endif /* LIBC_SCCS and not lint */ - #include "namespace.h" #include #define _NS_PRIVATE #include #include #include #include "un-namespace.h" #include "nsparser.h" %} %option never-interactive %option noinput %option nounput %option yylineno BLANK [ \t] CR \n STRING [a-zA-Z][a-zA-Z0-9_]* %% {BLANK}+ ; /* skip whitespace */ #.* ; /* skip comments */ \\{CR} ; /* allow continuation */ {CR} return NL; [sS][uU][cC][cC][eE][sS][sS] return SUCCESS; [uU][nN][aA][vV][aA][iI][lL] return UNAVAIL; [nN][oO][tT][fF][oO][uU][nN][dD] return NOTFOUND; [tT][rR][yY][aA][gG][aA][iI][nN] return TRYAGAIN; [rR][eE][tT][uU][rR][nN] return RETURN; [cC][oO][nN][tT][iI][nN][uU][eE] return CONTINUE; {STRING} { char *p; int i; if ((p = strdup(yytext)) == NULL) { syslog(LOG_ERR, "NSSWITCH(nslexer): memory allocation failure"); return ERRORTOKEN; } for (i = 0; i < strlen(p); i++) { if (isupper((unsigned char)p[i])) p[i] = tolower((unsigned char)p[i]); } _nsyylval.str = p; return STRING; } . return yytext[0]; %% #undef _nsyywrap int _nsyywrap(void) { return 1; } /* _nsyywrap */ void _nsyyerror(const char *msg) { syslog(LOG_ERR, "NSSWITCH(nslexer): %s line %d: %s at '%s'", _PATH_NS_CONF, yylineno, msg, yytext); } /* _nsyyerror */ diff --git a/lib/libc/powerpc/gen/syncicache.c b/lib/libc/powerpc/gen/syncicache.c index 5192d1356153..6376cb0e576d 100644 --- a/lib/libc/powerpc/gen/syncicache.c +++ b/lib/libc/powerpc/gen/syncicache.c @@ -1,105 +1,100 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (C) 1995-1997, 1999 Wolfgang Solfrank. * Copyright (C) 1995-1997, 1999 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $NetBSD: syncicache.c,v 1.2 1999/05/05 12:36:40 tsubai Exp $ */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #if defined(_KERNEL) || defined(_STANDALONE) #include #include #include #endif #include #include #include #ifdef _STANDALONE int cacheline_size = 32; #endif #if !defined(_KERNEL) && !defined(_STANDALONE) #include int cacheline_size = 0; static void getcachelinesize(void); static void getcachelinesize() { static int cachemib[] = { CTL_MACHDEP, CPU_CACHELINE }; int clen; clen = sizeof(cacheline_size); if (sysctl(cachemib, nitems(cachemib), &cacheline_size, &clen, NULL, 0) < 0 || !cacheline_size) { abort(); } } #endif void __syncicache(void *from, int len) { int l, off; char *p; #if !defined(_KERNEL) && !defined(_STANDALONE) if (!cacheline_size) getcachelinesize(); #endif off = (u_int)from & (cacheline_size - 1); l = len += off; p = (char *)from - off; do { __asm __volatile ("dcbst 0,%0" :: "r"(p)); p += cacheline_size; } while ((l -= cacheline_size) > 0); __asm __volatile ("sync"); p = (char *)from - off; do { __asm __volatile ("icbi 0,%0" :: "r"(p)); p += cacheline_size; } while ((len -= cacheline_size) > 0); __asm __volatile ("sync; isync"); } diff --git a/lib/libc/powerpc64/gen/syncicache.c b/lib/libc/powerpc64/gen/syncicache.c index d96529bc3833..7885a36bd1d1 100644 --- a/lib/libc/powerpc64/gen/syncicache.c +++ b/lib/libc/powerpc64/gen/syncicache.c @@ -1,105 +1,100 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (C) 1995-1997, 1999 Wolfgang Solfrank. * Copyright (C) 1995-1997, 1999 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $NetBSD: syncicache.c,v 1.2 1999/05/05 12:36:40 tsubai Exp $ */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #if defined(_KERNEL) || defined(_STANDALONE) #include #include #include #endif #include #include #include #ifdef _STANDALONE int cacheline_size = 32; #endif #if !defined(_KERNEL) && !defined(_STANDALONE) #include int cacheline_size = 0; static void getcachelinesize(void); static void getcachelinesize() { static int cachemib[] = { CTL_MACHDEP, CPU_CACHELINE }; long clen; clen = sizeof(cacheline_size); if (sysctl(cachemib, nitems(cachemib), &cacheline_size, &clen, NULL, 0) < 0 || !cacheline_size) { abort(); } } #endif void __syncicache(void *from, int len) { off_t l, off; char *p; #if !defined(_KERNEL) && !defined(_STANDALONE) if (!cacheline_size) getcachelinesize(); #endif off = (uintptr_t)from & (cacheline_size - 1); l = len += off; p = (char *)from - off; do { __asm __volatile ("dcbst 0,%0" :: "r"(p)); p += cacheline_size; } while ((l -= cacheline_size) > 0); __asm __volatile ("sync"); p = (char *)from - off; do { __asm __volatile ("icbi 0,%0" :: "r"(p)); p += cacheline_size; } while ((len -= cacheline_size) > 0); __asm __volatile ("sync; isync"); } diff --git a/lib/msun/src/e_sqrtf.c b/lib/msun/src/e_sqrtf.c index 1fd0cec447fb..f9e2a320f20e 100644 --- a/lib/msun/src/e_sqrtf.c +++ b/lib/msun/src/e_sqrtf.c @@ -1,97 +1,93 @@ /* e_sqrtf.c -- float version of e_sqrt.c. * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. */ /* * ==================================================== * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. * * Developed at SunPro, a Sun Microsystems, Inc. business. * Permission to use, copy, modify, and distribute this * software is freely granted, provided that this notice * is preserved. * ==================================================== */ -#ifndef lint -static char rcsid[] = "$FreeBSD$"; -#endif - #include "math.h" #include "math_private.h" #ifdef USE_BUILTIN_SQRTF float sqrtf(float x) { return (__builtin_sqrtf(x)); } #else static const float one = 1.0, tiny=1.0e-30; float sqrtf(float x) { float z; int32_t sign = (int)0x80000000; int32_t ix,s,q,m,t,i; u_int32_t r; GET_FLOAT_WORD(ix,x); /* take care of Inf and NaN */ if((ix&0x7f800000)==0x7f800000) { return x*x+x; /* sqrt(NaN)=NaN, sqrt(+inf)=+inf sqrt(-inf)=sNaN */ } /* take care of zero */ if(ix<=0) { if((ix&(~sign))==0) return x;/* sqrt(+-0) = +-0 */ else if(ix<0) return (x-x)/(x-x); /* sqrt(-ve) = sNaN */ } /* normalize x */ m = (ix>>23); if(m==0) { /* subnormal x */ for(i=0;(ix&0x00800000)==0;i++) ix<<=1; m -= i-1; } m -= 127; /* unbias exponent */ ix = (ix&0x007fffff)|0x00800000; if(m&1) /* odd m, double x to make it even */ ix += ix; m >>= 1; /* m = [m/2] */ /* generate sqrt(x) bit by bit */ ix += ix; q = s = 0; /* q = sqrt(x) */ r = 0x01000000; /* r = moving bit from right to left */ while(r!=0) { t = s+r; if(t<=ix) { s = t+r; ix -= t; q += r; } ix += ix; r>>=1; } /* use floating add to find out rounding direction */ if(ix!=0) { z = one-tiny; /* trigger inexact flag */ if (z>=one) { z = one+tiny; if (z>one) q += 2; else q += (q&1); } } ix = (q>>1)+0x3f000000; ix += (m <<23); SET_FLOAT_WORD(z,ix); return z; } #endif diff --git a/lib/msun/src/s_modf.c b/lib/msun/src/s_modf.c index ab13191b9004..ffb1702751fa 100644 --- a/lib/msun/src/s_modf.c +++ b/lib/msun/src/s_modf.c @@ -1,79 +1,75 @@ /* @(#)s_modf.c 5.1 93/09/24 */ /* * ==================================================== * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. * * Developed at SunPro, a Sun Microsystems, Inc. business. * Permission to use, copy, modify, and distribute this * software is freely granted, provided that this notice * is preserved. * ==================================================== */ -#ifndef lint -static char rcsid[] = "$FreeBSD$"; -#endif - /* * modf(double x, double *iptr) * return fraction part of x, and return x's integral part in *iptr. * Method: * Bit twiddling. * * Exception: * No exception. */ #include "math.h" #include "math_private.h" static const double one = 1.0; double modf(double x, double *iptr) { int32_t i0,i1,j0; u_int32_t i; EXTRACT_WORDS(i0,i1,x); j0 = ((i0>>20)&0x7ff)-0x3ff; /* exponent of x */ if(j0<20) { /* integer part in high x */ if(j0<0) { /* |x|<1 */ INSERT_WORDS(*iptr,i0&0x80000000,0); /* *iptr = +-0 */ return x; } else { i = (0x000fffff)>>j0; if(((i0&i)|i1)==0) { /* x is integral */ u_int32_t high; *iptr = x; GET_HIGH_WORD(high,x); INSERT_WORDS(x,high&0x80000000,0); /* return +-0 */ return x; } else { INSERT_WORDS(*iptr,i0&(~i),0); return x - *iptr; } } } else if (j0>51) { /* no fraction part */ u_int32_t high; if (j0 == 0x400) { /* inf/NaN */ *iptr = x; return 0.0 / x; } *iptr = x*one; GET_HIGH_WORD(high,x); INSERT_WORDS(x,high&0x80000000,0); /* return +-0 */ return x; } else { /* fraction part in low x */ i = ((u_int32_t)(0xffffffff))>>(j0-20); if((i1&i)==0) { /* x is integral */ u_int32_t high; *iptr = x; GET_HIGH_WORD(high,x); INSERT_WORDS(x,high&0x80000000,0); /* return +-0 */ return x; } else { INSERT_WORDS(*iptr,i0,i1&(~i)); return x - *iptr; } } } diff --git a/lib/msun/src/w_cabsf.c b/lib/msun/src/w_cabsf.c index b5065c8a5683..aedbdef217ca 100644 --- a/lib/msun/src/w_cabsf.c +++ b/lib/msun/src/w_cabsf.c @@ -1,22 +1,17 @@ /* * cabsf() wrapper for hypotf(). * * Written by J.T. Conklin, * Placed into the Public Domain, 1994. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include "math_private.h" float cabsf(float complex z) { return hypotf(crealf(z), cimagf(z)); } diff --git a/libexec/atrun/atrun.c b/libexec/atrun/atrun.c index e9e49146ba7a..ee312591ccd4 100644 --- a/libexec/atrun/atrun.c +++ b/libexec/atrun/atrun.c @@ -1,593 +1,588 @@ /*- * atrun.c - run jobs queued by at; run with root privileges. * * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 1993, 1994 Thomas Koenig * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. The name of the author(s) may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - /* System Headers */ #include #include #include #include #ifdef __FreeBSD__ #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __FreeBSD__ #include #else #include #endif #ifdef LOGIN_CAP #include #endif #ifdef PAM #include #include #endif /* Local headers */ #include "gloadavg.h" #define MAIN #include "privs.h" /* Macros */ #ifndef ATJOB_DIR #define ATJOB_DIR "/usr/spool/atjobs/" #endif #ifndef ATSPOOL_DIR #define ATSPOOL_DIR "/usr/spool/atspool/" #endif #ifndef LOADAVG_MX #define LOADAVG_MX 1.5 #endif /* File scope variables */ static const char * const atrun = "atrun"; /* service name for syslog etc. */ static int debug = 0; void perr(const char *fmt, ...); void perrx(const char *fmt, ...); static void usage(void) __dead2; /* Local functions */ static int write_string(int fd, const char* a) { return write(fd, a, strlen(a)); } #undef DEBUG_FORK #ifdef DEBUG_FORK static pid_t myfork(void) { pid_t res; res = fork(); if (res == 0) kill(getpid(),SIGSTOP); return res; } #define fork myfork #endif static void run_file(const char *filename, uid_t uid, gid_t gid) { /* Run a file by spawning off a process which redirects I/O, * spawns a subshell, then waits for it to complete and sends * mail to the user. */ pid_t pid; int fd_out, fd_in; int queue; char mailbuf[MAXLOGNAME], fmt[64]; char *mailname = NULL; FILE *stream; int send_mail = 0; struct stat buf, lbuf; off_t size; struct passwd *pentry; int fflags; long nuid; long ngid; #ifdef PAM pam_handle_t *pamh = NULL; int pam_err; struct pam_conv pamc = { .conv = openpam_nullconv, .appdata_ptr = NULL }; #endif PRIV_START if (chmod(filename, S_IRUSR) != 0) { perr("cannot change file permissions"); } PRIV_END pid = fork(); if (pid == -1) perr("cannot fork"); else if (pid != 0) return; /* Let's see who we mail to. Hopefully, we can read it from * the command file; if not, send it to the owner, or, failing that, * to root. */ pentry = getpwuid(uid); if (pentry == NULL) perrx("Userid %lu not found - aborting job %s", (unsigned long) uid, filename); #ifdef PAM PRIV_START pam_err = pam_start(atrun, pentry->pw_name, &pamc, &pamh); if (pam_err != PAM_SUCCESS) perrx("cannot start PAM: %s", pam_strerror(pamh, pam_err)); pam_err = pam_acct_mgmt(pamh, PAM_SILENT); /* Expired password shouldn't prevent the job from running. */ if (pam_err != PAM_SUCCESS && pam_err != PAM_NEW_AUTHTOK_REQD) perrx("Account %s (userid %lu) unavailable for job %s: %s", pentry->pw_name, (unsigned long)uid, filename, pam_strerror(pamh, pam_err)); pam_end(pamh, pam_err); PRIV_END #endif /* PAM */ PRIV_START stream=fopen(filename, "r"); PRIV_END if (stream == NULL) perr("cannot open input file %s", filename); if ((fd_in = dup(fileno(stream))) <0) perr("error duplicating input file descriptor"); if (fstat(fd_in, &buf) == -1) perr("error in fstat of input file descriptor"); if (lstat(filename, &lbuf) == -1) perr("error in fstat of input file"); if (S_ISLNK(lbuf.st_mode)) perrx("Symbolic link encountered in job %s - aborting", filename); if ((lbuf.st_dev != buf.st_dev) || (lbuf.st_ino != buf.st_ino) || (lbuf.st_uid != buf.st_uid) || (lbuf.st_gid != buf.st_gid) || (lbuf.st_size!=buf.st_size)) perrx("Somebody changed files from under us for job %s - aborting", filename); if (buf.st_nlink > 1) perrx("Somebody is trying to run a linked script for job %s", filename); if ((fflags = fcntl(fd_in, F_GETFD)) <0) perr("error in fcntl"); fcntl(fd_in, F_SETFD, fflags & ~FD_CLOEXEC); snprintf(fmt, sizeof(fmt), "#!/bin/sh\n# atrun uid=%%ld gid=%%ld\n# mail %%%ds %%d", MAXLOGNAME - 1); if (fscanf(stream, fmt, &nuid, &ngid, mailbuf, &send_mail) != 4) perrx("File %s is in wrong format - aborting", filename); if (mailbuf[0] == '-') perrx("Illegal mail name %s in %s", mailbuf, filename); mailname = mailbuf; if (nuid != uid) perrx("Job %s - userid %ld does not match file uid %lu", filename, nuid, (unsigned long)uid); if (ngid != gid) perrx("Job %s - groupid %ld does not match file gid %lu", filename, ngid, (unsigned long)gid); fclose(stream); if (chdir(ATSPOOL_DIR) < 0) perr("cannot chdir to %s", ATSPOOL_DIR); /* Create a file to hold the output of the job we are about to run. * Write the mail header. */ if((fd_out=open(filename, O_WRONLY | O_CREAT | O_EXCL, S_IWUSR | S_IRUSR)) < 0) perr("cannot create output file"); write_string(fd_out, "Subject: Output from your job "); write_string(fd_out, filename); write_string(fd_out, "\n\n"); fstat(fd_out, &buf); size = buf.st_size; close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); pid = fork(); if (pid < 0) perr("error in fork"); else if (pid == 0) { char *nul = NULL; char **nenvp = &nul; /* Set up things for the child; we want standard input from the input file, * and standard output and error sent to our output file. */ if (lseek(fd_in, (off_t) 0, SEEK_SET) < 0) perr("error in lseek"); if (dup(fd_in) != STDIN_FILENO) perr("error in I/O redirection"); if (dup(fd_out) != STDOUT_FILENO) perr("error in I/O redirection"); if (dup(fd_out) != STDERR_FILENO) perr("error in I/O redirection"); close(fd_in); close(fd_out); if (chdir(ATJOB_DIR) < 0) perr("cannot chdir to %s", ATJOB_DIR); queue = *filename; PRIV_START nice(tolower(queue) - 'a'); #ifdef LOGIN_CAP /* * For simplicity and safety, set all aspects of the user context * except for a selected subset: Don't set priority, which was * set based on the queue file name according to the tradition. * Don't bother to set environment, including path vars, either * because it will be discarded anyway. Although the job file * should set umask, preset it here just in case. */ if (setusercontext(NULL, pentry, uid, LOGIN_SETALL & ~(LOGIN_SETPRIORITY | LOGIN_SETPATH | LOGIN_SETENV)) != 0) exit(EXIT_FAILURE); /* setusercontext() logged the error */ #else /* LOGIN_CAP */ if (initgroups(pentry->pw_name,pentry->pw_gid)) perr("cannot init group access list"); if (setgid(gid) < 0 || setegid(pentry->pw_gid) < 0) perr("cannot change group"); if (setlogin(pentry->pw_name)) perr("cannot set login name"); if (setuid(uid) < 0 || seteuid(uid) < 0) perr("cannot set user id"); #endif /* LOGIN_CAP */ if (chdir(pentry->pw_dir)) chdir("/"); if(execle("/bin/sh","sh",(char *) NULL, nenvp) != 0) perr("exec failed for /bin/sh"); PRIV_END } /* We're the parent. Let's wait. */ close(fd_in); close(fd_out); waitpid(pid, (int *) NULL, 0); /* Send mail. Unlink the output file first, so it is deleted after * the run. */ stat(filename, &buf); if (open(filename, O_RDONLY) != STDIN_FILENO) perr("open of jobfile failed"); unlink(filename); if ((buf.st_size != size) || send_mail) { PRIV_START #ifdef LOGIN_CAP /* * This time set full context to run the mailer. */ if (setusercontext(NULL, pentry, uid, LOGIN_SETALL) != 0) exit(EXIT_FAILURE); /* setusercontext() logged the error */ #else /* LOGIN_CAP */ if (initgroups(pentry->pw_name,pentry->pw_gid)) perr("cannot init group access list"); if (setgid(gid) < 0 || setegid(pentry->pw_gid) < 0) perr("cannot change group"); if (setlogin(pentry->pw_name)) perr("cannot set login name"); if (setuid(uid) < 0 || seteuid(uid) < 0) perr("cannot set user id"); #endif /* LOGIN_CAP */ if (chdir(pentry->pw_dir)) chdir("/"); #ifdef __FreeBSD__ execl(_PATH_SENDMAIL, "sendmail", "-F", "Atrun Service", "-odi", "-oem", mailname, (char *) NULL); #else execl(MAIL_CMD, MAIL_CMD, mailname, (char *) NULL); #endif perr("exec failed for mail command"); PRIV_END } exit(EXIT_SUCCESS); } /* Global functions */ /* Needed in gloadavg.c */ void perr(const char *fmt, ...) { const char * const fmtadd = ": %m"; char nfmt[strlen(fmt) + strlen(fmtadd) + 1]; va_list ap; va_start(ap, fmt); if (debug) { vwarn(fmt, ap); } else { snprintf(nfmt, sizeof(nfmt), "%s%s", fmt, fmtadd); vsyslog(LOG_ERR, nfmt, ap); } va_end(ap); exit(EXIT_FAILURE); } void perrx(const char *fmt, ...) { va_list ap; va_start(ap, fmt); if (debug) vwarnx(fmt, ap); else vsyslog(LOG_ERR, fmt, ap); va_end(ap); exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { /* Browse through ATJOB_DIR, checking all the jobfiles wether they should * be executed and or deleted. The queue is coded into the first byte of * the job filename, the date (in minutes since Eon) as a hex number in the * following eight bytes, followed by a dot and a serial number. A file * which has not been executed yet is denoted by its execute - bit set. * For those files which are to be executed, run_file() is called, which forks * off a child which takes care of I/O redirection, forks off another child * for execution and yet another one, optionally, for sending mail. * Files which already have run are removed during the next invocation. */ DIR *spool; struct dirent *dirent; struct stat buf; unsigned long ctm; unsigned long jobno; char queue; time_t now, run_time; char batch_name[] = "Z2345678901234"; uid_t batch_uid; gid_t batch_gid; int c; int run_batch; #ifdef __FreeBSD__ size_t ncpusz; double load_avg = -1; int ncpu; #else double load_avg = LOADAVG_MX; #endif /* We don't need root privileges all the time; running under uid and gid daemon * is fine. */ RELINQUISH_PRIVS_ROOT(DAEMON_UID, DAEMON_GID) openlog(atrun, LOG_PID, LOG_CRON); opterr = 0; while((c=getopt(argc, argv, "dl:"))!= -1) { switch (c) { case 'l': if (sscanf(optarg, "%lf", &load_avg) != 1) perr("garbled option -l"); #ifndef __FreeBSD__ if (load_avg <= 0.) load_avg = LOADAVG_MX; #endif break; case 'd': debug ++; break; case '?': default: usage(); } } if (chdir(ATJOB_DIR) != 0) perr("cannot change to %s", ATJOB_DIR); #ifdef __FreeBSD__ if (load_avg <= 0.) { ncpusz = sizeof(size_t); if (sysctlbyname("hw.ncpu", &ncpu, &ncpusz, NULL, 0) < 0) ncpu = 1; load_avg = LOADAVG_MX * ncpu; } #endif /* Main loop. Open spool directory for reading and look over all the * files in there. If the filename indicates that the job should be run * and the x bit is set, fork off a child which sets its user and group * id to that of the files and exec a /bin/sh which executes the shell * script. Unlink older files if they should no longer be run. For * deletion, their r bit has to be turned on. * * Also, pick the oldest batch job to run, at most one per invocation of * atrun. */ if ((spool = opendir(".")) == NULL) perr("cannot read %s", ATJOB_DIR); if (flock(dirfd(spool), LOCK_EX) == -1) perr("cannot lock %s", ATJOB_DIR); now = time(NULL); run_batch = 0; batch_uid = (uid_t) -1; batch_gid = (gid_t) -1; while ((dirent = readdir(spool)) != NULL) { if (stat(dirent->d_name,&buf) != 0) perr("cannot stat in %s", ATJOB_DIR); /* We don't want directories */ if (!S_ISREG(buf.st_mode)) continue; if (sscanf(dirent->d_name,"%c%5lx%8lx",&queue,&jobno,&ctm) != 3) continue; run_time = (time_t) ctm*60; if ((S_IXUSR & buf.st_mode) && (run_time <=now)) { if (isupper(queue) && (strcmp(batch_name,dirent->d_name) > 0)) { run_batch = 1; strlcpy(batch_name, dirent->d_name, sizeof(batch_name)); batch_uid = buf.st_uid; batch_gid = buf.st_gid; } /* The file is executable and old enough */ if (islower(queue)) run_file(dirent->d_name, buf.st_uid, buf.st_gid); } /* Delete older files */ if ((run_time < now) && !(S_IXUSR & buf.st_mode) && (S_IRUSR & buf.st_mode)) unlink(dirent->d_name); } /* run the single batch file, if any */ if (run_batch && (gloadavg() < load_avg)) run_file(batch_name, batch_uid, batch_gid); if (flock(dirfd(spool), LOCK_UN) == -1) perr("cannot unlock %s", ATJOB_DIR); if (closedir(spool) == -1) perr("cannot closedir %s", ATJOB_DIR); closelog(); exit(EXIT_SUCCESS); } static void usage(void) { if (debug) fprintf(stderr, "usage: atrun [-l load_avg] [-d]\n"); else syslog(LOG_ERR, "usage: atrun [-l load_avg] [-d]"); exit(EXIT_FAILURE); } diff --git a/libexec/atrun/gloadavg.c b/libexec/atrun/gloadavg.c index 86651aac3e67..e513183a391c 100644 --- a/libexec/atrun/gloadavg.c +++ b/libexec/atrun/gloadavg.c @@ -1,74 +1,69 @@ /*- * gloadavg.c - get load average for Linux * Copyright (C) 1993 Thomas Koenig * * SPDX-License-Identifier: BSD-2-Clause * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. The name of the author(s) may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #ifndef __FreeBSD__ #define _POSIX_SOURCE 1 /* System Headers */ #include #else #include #endif /* Local headers */ #include "gloadavg.h" /* Global functions */ void perr(const char *fmt, ...); double gloadavg(void) /* return the current load average as a floating point number, or <0 for * error */ { double result; #ifndef __FreeBSD__ FILE *fp; if((fp=fopen(PROC_DIR "loadavg","r")) == NULL) result = -1.0; else { if(fscanf(fp,"%lf",&result) != 1) result = -1.0; fclose(fp); } #else if (getloadavg(&result, 1) != 1) perr("error in getloadavg"); #endif return result; } diff --git a/libexec/atrun/gloadavg.h b/libexec/atrun/gloadavg.h index f0ae6b45e868..a202cf0b3700 100644 --- a/libexec/atrun/gloadavg.h +++ b/libexec/atrun/gloadavg.h @@ -1,31 +1,28 @@ /*- * gloadavg.h - header for atrun(8) * Copyright (C) 1993 Thomas Koenig * * SPDX-License-Identifier: BSD-2-Clause * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. The name of the author(s) may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ double gloadavg(void); -#if 0 -static char atrun_h_rcsid[] = "$FreeBSD$"; -#endif diff --git a/libexec/comsat/comsat.c b/libexec/comsat/comsat.c index c9d490d2cf6e..138881db9e4a 100644 --- a/libexec/comsat/comsat.c +++ b/libexec/comsat/comsat.c @@ -1,286 +1,284 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1980, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)comsat.c 8.1 (Berkeley) 6/4/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int debug = 0; #define dsyslog if (debug) syslog #define MAXIDLE 120 static char hostname[MAXHOSTNAMELEN]; static void jkfprintf(FILE *, char[], char[], off_t); static void mailfor(char *); static void notify(struct utmpx *, char[], off_t, int); static void reapchildren(int); int main(int argc __unused, char *argv[] __unused) { struct sockaddr_in from; socklen_t fromlen; int cc; char msgbuf[256]; /* verify proper invocation */ fromlen = sizeof(from); if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) err(1, "getsockname"); openlog("comsat", LOG_PID, LOG_DAEMON); if (chdir(_PATH_MAILDIR)) { syslog(LOG_ERR, "chdir: %s: %m", _PATH_MAILDIR); (void) recv(0, msgbuf, sizeof(msgbuf) - 1, 0); exit(1); } (void)gethostname(hostname, sizeof(hostname)); (void)signal(SIGTTOU, SIG_IGN); (void)signal(SIGCHLD, reapchildren); for (;;) { cc = recv(0, msgbuf, sizeof(msgbuf) - 1, 0); if (cc <= 0) { if (errno != EINTR) sleep(1); errno = 0; continue; } msgbuf[cc] = '\0'; mailfor(msgbuf); sigsetmask(0L); } } static void reapchildren(int signo __unused) { while (wait3(NULL, WNOHANG, NULL) > 0); } static void mailfor(char *name) { struct utmpx *utp; char *cp; char *file; off_t offset; int folder; char buf[sizeof(_PATH_MAILDIR) + sizeof(utp->ut_user) + 1]; char buf2[sizeof(_PATH_MAILDIR) + sizeof(utp->ut_user) + 1]; if (!(cp = strchr(name, '@'))) return; *cp = '\0'; offset = strtoll(cp + 1, NULL, 10); if (!(cp = strchr(cp + 1, ':'))) file = name; else file = cp + 1; sprintf(buf, "%s/%.*s", _PATH_MAILDIR, (int)sizeof(utp->ut_user), name); if (*file != '/') { sprintf(buf2, "%s/%.*s", _PATH_MAILDIR, (int)sizeof(utp->ut_user), file); file = buf2; } folder = strcmp(buf, file); setutxent(); while ((utp = getutxent()) != NULL) if (utp->ut_type == USER_PROCESS && !strcmp(utp->ut_user, name)) notify(utp, file, offset, folder); endutxent(); } static const char *cr; static void notify(struct utmpx *utp, char file[], off_t offset, int folder) { FILE *tp; struct stat stb; struct termios tio; char tty[20]; const char *s = utp->ut_line; if (strncmp(s, "pts/", 4) == 0) s += 4; if (strchr(s, '/')) { /* A slash is an attempt to break security... */ syslog(LOG_AUTH | LOG_NOTICE, "Unexpected `/' in `%s'", utp->ut_line); return; } (void)snprintf(tty, sizeof(tty), "%s%.*s", _PATH_DEV, (int)sizeof(utp->ut_line), utp->ut_line); if (stat(tty, &stb) == -1 || !(stb.st_mode & (S_IXUSR | S_IXGRP))) { dsyslog(LOG_DEBUG, "%s: wrong mode on %s", utp->ut_user, tty); return; } dsyslog(LOG_DEBUG, "notify %s on %s", utp->ut_user, tty); switch (fork()) { case -1: syslog(LOG_NOTICE, "fork failed (%m)"); return; case 0: break; default: return; } if ((tp = fopen(tty, "w")) == NULL) { dsyslog(LOG_ERR, "%s: %s", tty, strerror(errno)); _exit(1); } (void)tcgetattr(fileno(tp), &tio); cr = ((tio.c_oflag & (OPOST|ONLCR)) == (OPOST|ONLCR)) ? "\n" : "\n\r"; switch (stb.st_mode & (S_IXUSR | S_IXGRP)) { case S_IXUSR: case (S_IXUSR | S_IXGRP): (void)fprintf(tp, "%s\007New mail for %s@%.*s\007 has arrived%s%s%s:%s----%s", cr, utp->ut_user, (int)sizeof(hostname), hostname, folder ? cr : "", folder ? "to " : "", folder ? file : "", cr, cr); jkfprintf(tp, utp->ut_user, file, offset); break; case S_IXGRP: (void)fprintf(tp, "\007"); (void)fflush(tp); (void)sleep(1); (void)fprintf(tp, "\007"); break; default: break; } (void)fclose(tp); _exit(0); } static void jkfprintf(FILE *tp, char user[], char file[], off_t offset) { unsigned char *cp, ch; FILE *fi; int linecnt, charcnt, inheader; struct passwd *p; unsigned char line[BUFSIZ]; /* Set effective uid to user in case mail drop is on nfs */ if ((p = getpwnam(user)) != NULL) (void) setuid(p->pw_uid); if ((fi = fopen(file, "r")) == NULL) return; (void)fseeko(fi, offset, SEEK_CUR); /* * Print the first 7 lines or 560 characters of the new mail * (whichever comes first). Skip header crap other than * From, Subject, To, and Date. */ linecnt = 7; charcnt = 560; inheader = 1; while (fgets(line, sizeof(line), fi) != NULL) { if (inheader) { if (line[0] == '\n') { inheader = 0; continue; } if (line[0] == ' ' || line[0] == '\t' || (strncmp(line, "From:", 5) && strncmp(line, "Subject:", 8))) continue; } if (linecnt <= 0 || charcnt <= 0) { (void)fprintf(tp, "...more...%s", cr); (void)fclose(fi); return; } /* strip weird stuff so can't trojan horse stupid terminals */ for (cp = line; (ch = *cp) && ch != '\n'; ++cp, --charcnt) { /* disable upper controls and enable all other 8bit codes due to lack of locale knowledge */ if (((ch & 0x80) && ch < 0xA0) || (!(ch & 0x80) && !isprint(ch) && !isspace(ch) && ch != '\a' && ch != '\b') ) { if (ch & 0x80) { ch &= ~0x80; (void)fputs("M-", tp); } if (iscntrl(ch)) { ch ^= 0x40; (void)fputc('^', tp); } } (void)fputc(ch, tp); } (void)fputs(cr, tp); --linecnt; } (void)fprintf(tp, "----%s\n", cr); (void)fclose(fi); } diff --git a/libexec/fingerd/fingerd.c b/libexec/fingerd/fingerd.c index ff8d5046a577..dd51064a00a6 100644 --- a/libexec/fingerd/fingerd.c +++ b/libexec/fingerd/fingerd.c @@ -1,245 +1,243 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1983, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)fingerd.c 8.1 (Berkeley) 6/4/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pathnames.h" #ifdef USE_BLACKLIST #include #endif void logerr(const char *, ...) __printflike(1, 2) __dead2; int main(int argc, char *argv[]) { FILE *fp; int ch; char *lp; struct sockaddr_storage ss; socklen_t sval; int p[2], debug, kflag, logging, pflag, secure; #define ENTRIES 50 char **ap, *av[ENTRIES + 1], **comp, line[1024], *prog; char rhost[MAXHOSTNAMELEN]; prog = _PATH_FINGER; debug = logging = kflag = pflag = secure = 0; openlog("fingerd", LOG_PID | LOG_CONS, LOG_DAEMON); opterr = 0; while ((ch = getopt(argc, argv, "dklp:s")) != -1) switch (ch) { case 'd': debug = 1; break; case 'k': kflag = 1; break; case 'l': logging = 1; break; case 'p': prog = optarg; pflag = 1; break; case 's': secure = 1; break; case '?': default: logerr("illegal option -- %c", optopt); } /* * Enable server-side Transaction TCP. */ if (!debug) { int one = 1; if (setsockopt(STDOUT_FILENO, IPPROTO_TCP, TCP_NOPUSH, &one, sizeof one) < 0) { logerr("setsockopt(TCP_NOPUSH) failed: %m"); } } if (!fgets(line, sizeof(line), stdin)) exit(1); if (!debug && (logging || pflag)) { sval = sizeof(ss); if (getpeername(0, (struct sockaddr *)&ss, &sval) < 0) logerr("getpeername: %s", strerror(errno)); realhostname_sa(rhost, sizeof rhost - 1, (struct sockaddr *)&ss, sval); rhost[sizeof(rhost) - 1] = '\0'; if (pflag) setenv("FINGERD_REMOTE_HOST", rhost, 1); } if (logging) { char *t; char *end; end = memchr(line, 0, sizeof(line)); if (end == NULL) { if ((t = malloc(sizeof(line) + 1)) == NULL) logerr("malloc: %s", strerror(errno)); memcpy(t, line, sizeof(line)); t[sizeof(line)] = 0; } else { if ((t = strdup(line)) == NULL) logerr("strdup: %s", strerror(errno)); } for (end = t; *end; end++) if (*end == '\n' || *end == '\r') *end = ' '; syslog(LOG_NOTICE, "query from %s: `%s'", rhost, t); } comp = &av[2]; av[3] = "--"; if (kflag) *comp-- = "-k"; for (lp = line, ap = &av[4];;) { *ap = strtok(lp, " \t\r\n"); if (!*ap) { if (secure && ap == &av[4]) { #ifdef USE_BLACKLIST blacklist(1, STDIN_FILENO, "nousername"); #endif puts("must provide username\r\n"); exit(1); } break; } if (secure && strchr(*ap, '@')) { #ifdef USE_BLACKLIST blacklist(1, STDIN_FILENO, "noforwarding"); #endif puts("forwarding service denied\r\n"); exit(1); } /* RFC742: "/[Ww]" == "-l" */ if ((*ap)[0] == '/' && ((*ap)[1] == 'W' || (*ap)[1] == 'w')) { *comp-- = "-l"; } else if (++ap == av + ENTRIES) { *ap = NULL; break; } lp = NULL; } if ((lp = strrchr(prog, '/')) != NULL) *comp = ++lp; else *comp = prog; if (pipe(p) < 0) logerr("pipe: %s", strerror(errno)); if (debug) { fprintf(stderr, "%s", prog); for (ap = comp; *ap != NULL; ++ap) fprintf(stderr, " %s", *ap); fprintf(stderr, "\n"); } switch(vfork()) { case 0: (void)close(p[0]); if (p[1] != STDOUT_FILENO) { (void)dup2(p[1], STDOUT_FILENO); (void)close(p[1]); } dup2(STDOUT_FILENO, STDERR_FILENO); #ifdef USE_BLACKLIST blacklist(0, STDIN_FILENO, "success"); #endif execv(prog, comp); write(STDERR_FILENO, prog, strlen(prog)); #define MSG ": cannot execute\n" write(STDERR_FILENO, MSG, strlen(MSG)); #undef MSG _exit(1); case -1: logerr("fork: %s", strerror(errno)); } (void)close(p[1]); if (!(fp = fdopen(p[0], "r"))) logerr("fdopen: %s", strerror(errno)); while ((ch = getc(fp)) != EOF) { if (ch == '\n') putchar('\r'); putchar(ch); } exit(0); } #include void logerr(const char *fmt, ...) { va_list ap; va_start(ap, fmt); (void)vsyslog(LOG_ERR, fmt, ap); va_end(ap); exit(1); /* NOTREACHED */ } diff --git a/libexec/getty/init.c b/libexec/getty/init.c index 79b9601a2be1..e09cbf2c3e94 100644 --- a/libexec/getty/init.c +++ b/libexec/getty/init.c @@ -1,156 +1,154 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)from: init.c 8.1 (Berkeley) 6/4/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ /* * Getty table initializations. * * Melbourne getty. */ #include #include #include "gettytab.h" #include "extern.h" #include "pathnames.h" static char loginmsg[] = "login: "; static char nullstr[] = ""; static char loginprg[] = _PATH_LOGIN; static char datefmt[] = "%+"; #define M(a) (char *)(&omode.c_cc[a]) struct gettystrs gettystrs[] = { { "nx", NULL, NULL }, /* next table */ { "cl", NULL, NULL }, /* screen clear characters */ { "im", NULL, NULL }, /* initial message */ { "lm", loginmsg, NULL }, /* login message */ { "er", M(VERASE), NULL }, /* erase character */ { "kl", M(VKILL), NULL }, /* kill character */ { "et", M(VEOF), NULL }, /* eof chatacter (eot) */ { "pc", nullstr, NULL }, /* pad character */ { "tt", NULL, NULL }, /* terminal type */ { "ev", NULL, NULL }, /* environment */ { "lo", loginprg, NULL }, /* login program */ { "hn", hostname, NULL }, /* host name */ { "he", NULL, NULL }, /* host name edit */ { "in", M(VINTR), NULL }, /* interrupt char */ { "qu", M(VQUIT), NULL }, /* quit char */ { "xn", M(VSTART), NULL }, /* XON (start) char */ { "xf", M(VSTOP), NULL }, /* XOFF (stop) char */ { "bk", M(VEOL), NULL }, /* brk char (alt \n) */ { "su", M(VSUSP), NULL }, /* suspend char */ { "ds", M(VDSUSP), NULL }, /* delayed suspend */ { "rp", M(VREPRINT), NULL }, /* reprint char */ { "fl", M(VDISCARD), NULL }, /* flush output */ { "we", M(VWERASE), NULL }, /* word erase */ { "ln", M(VLNEXT), NULL }, /* literal next */ { "Lo", NULL, NULL }, /* locale for strftime() */ { "pp", NULL, NULL }, /* ppp login program */ { "if", NULL, NULL }, /* sysv-like 'issue' filename */ { "ic", NULL, NULL }, /* modem init-chat */ { "ac", NULL, NULL }, /* modem answer-chat */ { "al", NULL, NULL }, /* user to auto-login */ { "df", datefmt, NULL }, /* format for strftime() */ { "iM" , NULL, NULL }, /* initial message program */ { NULL, NULL, NULL } }; struct gettynums gettynums[] = { { "is", 0, 0, 0 }, /* input speed */ { "os", 0, 0, 0 }, /* output speed */ { "sp", 0, 0, 0 }, /* both speeds */ { "nd", 0, 0, 0 }, /* newline delay */ { "cd", 0, 0, 0 }, /* carriage-return delay */ { "td", 0, 0, 0 }, /* tab delay */ { "fd", 0, 0, 0 }, /* form-feed delay */ { "bd", 0, 0, 0 }, /* backspace delay */ { "to", 0, 0, 0 }, /* timeout */ { "f0", 0, 0, 0 }, /* output flags */ { "f1", 0, 0, 0 }, /* input flags */ { "f2", 0, 0, 0 }, /* user mode flags */ { "pf", 0, 0, 0 }, /* delay before flush at 1st prompt */ { "c0", 0, 0, 0 }, /* output c_flags */ { "c1", 0, 0, 0 }, /* input c_flags */ { "c2", 0, 0, 0 }, /* user mode c_flags */ { "i0", 0, 0, 0 }, /* output i_flags */ { "i1", 0, 0, 0 }, /* input i_flags */ { "i2", 0, 0, 0 }, /* user mode i_flags */ { "l0", 0, 0, 0 }, /* output l_flags */ { "l1", 0, 0, 0 }, /* input l_flags */ { "l2", 0, 0, 0 }, /* user mode l_flags */ { "o0", 0, 0, 0 }, /* output o_flags */ { "o1", 0, 0, 0 }, /* input o_flags */ { "o2", 0, 0, 0 }, /* user mode o_flags */ { "de", 0, 0, 0 }, /* delay before sending 1st prompt */ { "rt", 0, 0, 0 }, /* reset timeout */ { "ct", 0, 0, 0 }, /* chat script timeout */ { "dc", 0, 0, 0 }, /* debug chat script value */ { NULL, 0, 0, 0 } }; struct gettyflags gettyflags[] = { { "ht", 0, 0, 0, 0 }, /* has tabs */ { "nl", 1, 0, 0, 0 }, /* has newline char */ { "ep", 0, 0, 0, 0 }, /* even parity */ { "op", 0, 0, 0, 0 }, /* odd parity */ { "ap", 0, 0, 0, 0 }, /* any parity */ { "ec", 1, 0, 0, 0 }, /* no echo */ { "co", 0, 0, 0, 0 }, /* console special */ { "cb", 0, 0, 0, 0 }, /* crt backspace */ { "ck", 0, 0, 0, 0 }, /* crt kill */ { "ce", 0, 0, 0, 0 }, /* crt erase */ { "pe", 0, 0, 0, 0 }, /* printer erase */ { "rw", 1, 0, 0, 0 }, /* don't use raw */ { "xc", 1, 0, 0, 0 }, /* don't ^X ctl chars */ { "lc", 0, 0, 0, 0 }, /* terminal las lower case */ { "uc", 0, 0, 0, 0 }, /* terminal has no lower case */ { "ig", 0, 0, 0, 0 }, /* ignore garbage */ { "ps", 0, 0, 0, 0 }, /* do port selector speed select */ { "hc", 1, 0, 0, 0 }, /* don't set hangup on close */ { "ub", 0, 0, 0, 0 }, /* unbuffered output */ { "ab", 0, 0, 0, 0 }, /* auto-baud detect with '\r' */ { "dx", 0, 0, 0, 0 }, /* set decctlq */ { "np", 0, 0, 0, 0 }, /* no parity at all (8bit chars) */ { "mb", 0, 0, 0, 0 }, /* do MDMBUF flow control */ { "hw", 0, 0, 0, 0 }, /* do CTSRTS flow control */ { "nc", 0, 0, 0, 0 }, /* set clocal (no carrier) */ { "pl", 0, 0, 0, 0 }, /* use PPP instead of login(1) */ { NULL, 0, 0, 0, 0 } }; diff --git a/libexec/getty/subr.c b/libexec/getty/subr.c index 68682df6d5bd..2c262e0968ca 100644 --- a/libexec/getty/subr.c +++ b/libexec/getty/subr.c @@ -1,684 +1,682 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)from: subr.c 8.1 (Berkeley) 6/4/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ /* * Melbourne getty. */ #include #include #include #include #include #include #include #include #include #include #include "gettytab.h" #include "pathnames.h" #include "extern.h" /* * Get a table entry. */ void gettable(const char *name) { char *buf = NULL; struct gettystrs *sp; struct gettynums *np; struct gettyflags *fp; long n; int l; char *p; static char path_gettytab[PATH_MAX]; char *dba[2]; static int firsttime = 1; strlcpy(path_gettytab, _PATH_GETTYTAB, sizeof(path_gettytab)); dba[0] = path_gettytab; dba[1] = NULL; if (firsttime) { /* * we need to strdup() anything in the strings array * initially in order to simplify things later */ for (sp = gettystrs; sp->field; sp++) if (sp->value != NULL) { /* handle these ones more carefully */ if (sp >= &gettystrs[4] && sp <= &gettystrs[6]) l = 2; else l = strlen(sp->value) + 1; if ((p = malloc(l)) != NULL) strlcpy(p, sp->value, l); /* * replace, even if NULL, else we'll * have problems with free()ing static mem */ sp->value = p; } firsttime = 0; } switch (cgetent(&buf, dba, name)) { case 1: syslog(LOG_ERR, "getty: couldn't resolve 'tc=' in gettytab '%s'", name); return; case 0: break; case -1: syslog(LOG_ERR, "getty: unknown gettytab entry '%s'", name); return; case -2: syslog(LOG_ERR, "getty: retrieving gettytab entry '%s': %m", name); return; case -3: syslog(LOG_ERR, "getty: recursive 'tc=' reference gettytab entry '%s'", name); return; default: syslog(LOG_ERR, "getty: unexpected cgetent() error for entry '%s'", name); return; } for (sp = gettystrs; sp->field; sp++) { if ((l = cgetstr(buf, sp->field, &p)) >= 0) { if (sp->value) { /* prefer existing value */ if (strcmp(p, sp->value) != 0) free(sp->value); else { free(p); p = sp->value; } } sp->value = p; } else if (l == -1) { free(sp->value); sp->value = NULL; } } for (np = gettynums; np->field; np++) { if (cgetnum(buf, np->field, &n) == -1) np->set = 0; else { np->set = 1; np->value = n; } } for (fp = gettyflags; fp->field; fp++) { if (cgetcap(buf, fp->field, ':') == NULL) fp->set = 0; else { fp->set = 1; fp->value = 1 ^ fp->invrt; } } free(buf); } void gendefaults(void) { struct gettystrs *sp; struct gettynums *np; struct gettyflags *fp; for (sp = gettystrs; sp->field; sp++) if (sp->value) sp->defalt = strdup(sp->value); for (np = gettynums; np->field; np++) if (np->set) np->defalt = np->value; for (fp = gettyflags; fp->field; fp++) if (fp->set) fp->defalt = fp->value; else fp->defalt = fp->invrt; } void setdefaults(void) { struct gettystrs *sp; struct gettynums *np; struct gettyflags *fp; for (sp = gettystrs; sp->field; sp++) if (!sp->value) sp->value = !sp->defalt ? sp->defalt : strdup(sp->defalt); for (np = gettynums; np->field; np++) if (!np->set) np->value = np->defalt; for (fp = gettyflags; fp->field; fp++) if (!fp->set) fp->value = fp->defalt; } static char ** charnames[] = { &ER, &KL, &IN, &QU, &XN, &XF, &ET, &BK, &SU, &DS, &RP, &FL, &WE, &LN, 0 }; #define CV(a) (char *)(&tmode.c_cc[a]) static char * charvars[] = { CV(VERASE), CV(VKILL), CV(VINTR), CV(VQUIT), CV(VSTART), CV(VSTOP), CV(VEOF), CV(VEOL), CV(VSUSP), CV(VDSUSP), CV(VREPRINT), CV(VDISCARD), CV(VWERASE), CV(VLNEXT), 0 }; void setchars(void) { int i; const char *p; for (i = 0; charnames[i]; i++) { p = *charnames[i]; if (p && *p) *charvars[i] = *p; else *charvars[i] = _POSIX_VDISABLE; } } /* Macros to clear/set/test flags. */ #define SET(t, f) (t) |= (f) #define CLR(t, f) (t) &= ~(f) #define ISSET(t, f) ((t) & (f)) void set_flags(int n) { tcflag_t iflag, oflag, cflag, lflag; switch (n) { case 0: if (C0set && I0set && L0set && O0set) { tmode.c_cflag = C0; tmode.c_iflag = I0; tmode.c_lflag = L0; tmode.c_oflag = O0; return; } break; case 1: if (C1set && I1set && L1set && O1set) { tmode.c_cflag = C1; tmode.c_iflag = I1; tmode.c_lflag = L1; tmode.c_oflag = O1; return; } break; default: if (C2set && I2set && L2set && O2set) { tmode.c_cflag = C2; tmode.c_iflag = I2; tmode.c_lflag = L2; tmode.c_oflag = O2; return; } break; } iflag = omode.c_iflag; oflag = omode.c_oflag; cflag = omode.c_cflag; lflag = omode.c_lflag; if (NP) { CLR(cflag, CSIZE|PARENB); SET(cflag, CS8); CLR(iflag, ISTRIP|INPCK|IGNPAR); } else if (AP || EP || OP) { CLR(cflag, CSIZE); SET(cflag, CS7|PARENB); SET(iflag, ISTRIP); if (OP && !EP) { SET(iflag, INPCK|IGNPAR); SET(cflag, PARODD); if (AP) CLR(iflag, INPCK); } else if (EP && !OP) { SET(iflag, INPCK|IGNPAR); CLR(cflag, PARODD); if (AP) CLR(iflag, INPCK); } else if (AP || (EP && OP)) { CLR(iflag, INPCK|IGNPAR); CLR(cflag, PARODD); } } /* else, leave as is */ #if 0 if (UC) f |= LCASE; #endif if (HC) SET(cflag, HUPCL); else CLR(cflag, HUPCL); if (MB) SET(cflag, MDMBUF); else CLR(cflag, MDMBUF); if (HW) SET(cflag, CRTSCTS); else CLR(cflag, CRTSCTS); if (NL) { SET(iflag, ICRNL); SET(oflag, ONLCR|OPOST); } else { CLR(iflag, ICRNL); CLR(oflag, ONLCR); } if (!HT) SET(oflag, OXTABS|OPOST); else CLR(oflag, OXTABS); #ifdef XXX_DELAY SET(f, delaybits()); #endif if (n == 1) { /* read mode flags */ if (RW) { iflag = 0; CLR(oflag, OPOST); CLR(cflag, CSIZE|PARENB); SET(cflag, CS8); lflag = 0; } else { CLR(lflag, ICANON); } goto out; } if (n == 0) goto out; #if 0 if (CB) SET(f, CRTBS); #endif if (CE) SET(lflag, ECHOE); else CLR(lflag, ECHOE); if (CK) SET(lflag, ECHOKE); else CLR(lflag, ECHOKE); if (PE) SET(lflag, ECHOPRT); else CLR(lflag, ECHOPRT); if (EC) SET(lflag, ECHO); else CLR(lflag, ECHO); if (XC) SET(lflag, ECHOCTL); else CLR(lflag, ECHOCTL); if (DX) SET(lflag, IXANY); else CLR(lflag, IXANY); out: tmode.c_iflag = iflag; tmode.c_oflag = oflag; tmode.c_cflag = cflag; tmode.c_lflag = lflag; } #ifdef XXX_DELAY struct delayval { unsigned delay; /* delay in ms */ int bits; }; /* * below are random guesses, I can't be bothered checking */ struct delayval crdelay[] = { { 1, CR1 }, { 2, CR2 }, { 3, CR3 }, { 83, CR1 }, { 166, CR2 }, { 0, CR3 }, }; struct delayval nldelay[] = { { 1, NL1 }, /* special, calculated */ { 2, NL2 }, { 3, NL3 }, { 100, NL2 }, { 0, NL3 }, }; struct delayval bsdelay[] = { { 1, BS1 }, { 0, 0 }, }; struct delayval ffdelay[] = { { 1, FF1 }, { 1750, FF1 }, { 0, FF1 }, }; struct delayval tbdelay[] = { { 1, TAB1 }, { 2, TAB2 }, { 3, XTABS }, /* this is expand tabs */ { 100, TAB1 }, { 0, TAB2 }, }; int delaybits(void) { int f; f = adelay(CD, crdelay); f |= adelay(ND, nldelay); f |= adelay(FD, ffdelay); f |= adelay(TD, tbdelay); f |= adelay(BD, bsdelay); return (f); } int adelay(int ms, struct delayval *dp) { if (ms == 0) return (0); while (dp->delay && ms > dp->delay) dp++; return (dp->bits); } #endif char editedhost[MAXHOSTNAMELEN]; void edithost(const char *pattern) { regex_t regex; regmatch_t *match; int found; if (pattern == NULL || *pattern == '\0') goto copyasis; if (regcomp(®ex, pattern, REG_EXTENDED) != 0) goto copyasis; match = calloc(regex.re_nsub + 1, sizeof(*match)); if (match == NULL) { regfree(®ex); goto copyasis; } found = !regexec(®ex, HN, regex.re_nsub + 1, match, 0); if (found) { size_t subex, totalsize; /* * We found a match. If there were no parenthesized * subexpressions in the pattern, use entire matched * string as ``editedhost''; otherwise use the first * matched subexpression. */ subex = !!regex.re_nsub; totalsize = match[subex].rm_eo - match[subex].rm_so + 1; strlcpy(editedhost, HN + match[subex].rm_so, totalsize > sizeof(editedhost) ? sizeof(editedhost) : totalsize); } free(match); regfree(®ex); if (found) return; /* * In case of any errors, or if the pattern did not match, pass * the original hostname as is. */ copyasis: strlcpy(editedhost, HN, sizeof(editedhost)); } static struct speedtab { int speed; int uxname; } speedtab[] = { { 50, B50 }, { 75, B75 }, { 110, B110 }, { 134, B134 }, { 150, B150 }, { 200, B200 }, { 300, B300 }, { 600, B600 }, { 1200, B1200 }, { 1800, B1800 }, { 2400, B2400 }, { 4800, B4800 }, { 9600, B9600 }, { 19200, EXTA }, { 19, EXTA }, /* for people who say 19.2K */ { 38400, EXTB }, { 38, EXTB }, { 7200, EXTB }, /* alternative */ { 57600, B57600 }, { 115200, B115200 }, { 230400, B230400 }, { 0, 0 } }; int speed(int val) { struct speedtab *sp; if (val <= B230400) return (val); for (sp = speedtab; sp->speed; sp++) if (sp->speed == val) return (sp->uxname); return (B300); /* default in impossible cases */ } void makeenv(char *env[]) { static char termbuf[128] = "TERM="; char *p, *q; char **ep; ep = env; if (TT && *TT) { strlcat(termbuf, TT, sizeof(termbuf)); *ep++ = termbuf; } if ((p = EV)) { q = p; while ((q = strchr(q, ','))) { *q++ = '\0'; *ep++ = p; p = q; } if (*p) *ep++ = p; } *ep = (char *)0; } /* * This speed select mechanism is written for the Develcon DATASWITCH. * The Develcon sends a string of the form "B{speed}\n" at a predefined * baud rate. This string indicates the user's actual speed. * The routine below returns the terminal type mapped from derived speed. */ static struct portselect { const char *ps_baud; const char *ps_type; } portspeeds[] = { { "B110", "std.110" }, { "B134", "std.134" }, { "B150", "std.150" }, { "B300", "std.300" }, { "B600", "std.600" }, { "B1200", "std.1200" }, { "B2400", "std.2400" }, { "B4800", "std.4800" }, { "B9600", "std.9600" }, { "B19200", "std.19200" }, { NULL, NULL } }; const char * portselector(void) { char c, baud[20]; const char *type = "default"; struct portselect *ps; size_t len; alarm(5*60); for (len = 0; len < sizeof (baud) - 1; len++) { if (read(STDIN_FILENO, &c, 1) <= 0) break; c &= 0177; if (c == '\n' || c == '\r') break; if (c == 'B') len = 0; /* in case of leading garbage */ baud[len] = c; } baud[len] = '\0'; for (ps = portspeeds; ps->ps_baud; ps++) if (strcmp(ps->ps_baud, baud) == 0) { type = ps->ps_type; break; } sleep(2); /* wait for connection to complete */ return (type); } /* * This auto-baud speed select mechanism is written for the Micom 600 * portselector. Selection is done by looking at how the character '\r' * is garbled at the different speeds. */ const char * autobaud(void) { struct pollfd set[1]; struct timespec timeout; char c; const char *type = "9600-baud"; (void)tcflush(0, TCIOFLUSH); set[0].fd = STDIN_FILENO; set[0].events = POLLIN; if (poll(set, 1, 5000) <= 0) return (type); if (read(STDIN_FILENO, &c, sizeof(char)) != sizeof(char)) return (type); timeout.tv_sec = 0; timeout.tv_nsec = 20000; (void)nanosleep(&timeout, NULL); (void)tcflush(0, TCIOFLUSH); switch (c & 0377) { case 0200: /* 300-baud */ type = "300-baud"; break; case 0346: /* 1200-baud */ type = "1200-baud"; break; case 015: /* 2400-baud */ case 0215: type = "2400-baud"; break; default: /* 4800-baud */ type = "4800-baud"; break; case 0377: /* 9600-baud */ type = "9600-baud"; break; } return (type); } diff --git a/libexec/mknetid/hash.c b/libexec/mknetid/hash.c index d340142aad90..5375b80fbe3b 100644 --- a/libexec/mknetid/hash.c +++ b/libexec/mknetid/hash.c @@ -1,170 +1,165 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1995 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include "hash.h" -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - /* * This hash function is stolen directly from the * Berkeley DB package. It already exists inside libc, but * it's declared static which prevents us from calling it * from here. */ /* * OZ's original sdbm hash */ u_int32_t hash(const void *keyarg, size_t len) { const u_char *key; size_t loop; u_int32_t h; #define HASHC h = *key++ + 65599 * h h = 0; key = keyarg; if (len > 0) { loop = (len + 8 - 1) >> 3; switch (len & (8 - 1)) { case 0: do { HASHC; /* FALLTHROUGH */ case 7: HASHC; /* FALLTHROUGH */ case 6: HASHC; /* FALLTHROUGH */ case 5: HASHC; /* FALLTHROUGH */ case 4: HASHC; /* FALLTHROUGH */ case 3: HASHC; /* FALLTHROUGH */ case 2: HASHC; /* FALLTHROUGH */ case 1: HASHC; } while (--loop); } } return (h); } /* * Generate a hash value for a given key (character string). * We mask off all but the lower 8 bits since our table array * can only hole 256 elements. */ u_int32_t hashkey(char *key) { if (key == NULL) return (-1); return(hash((void *)key, strlen(key)) & HASH_MASK); } /* Find an entry in the hash table (may be hanging off a linked list). */ struct grouplist *lookup(struct member_entry *table[], char *key) { struct member_entry *cur; cur = table[hashkey(key)]; while (cur) { if (!strcmp(cur->key, key)) return(cur->groups); cur = cur->next; } return(NULL); } struct grouplist dummy = { 99999, NULL }; /* * Store a group member entry and/or update its grouplist. */ void mstore (struct member_entry *table[], char *key, int gid, int dup) { struct member_entry *cur, *new; struct grouplist *tmp; u_int32_t i; i = hashkey(key); cur = table[i]; if (!dup) { tmp = (struct grouplist *)malloc(sizeof(struct grouplist)); tmp->groupid = gid; tmp->next = NULL; } /* Check if all we have to do is insert a new groupname. */ while (cur) { if (!dup && !strcmp(cur->key, key)) { tmp->next = cur->groups; cur->groups = tmp; return; } cur = cur->next; } /* Didn't find a match -- add the whole mess to the table. */ new = (struct member_entry *)malloc(sizeof(struct member_entry)); new->key = strdup(key); if (!dup) new->groups = tmp; else new->groups = (struct grouplist *)&dummy; new->next = table[i]; table[i] = new; return; } diff --git a/libexec/mknetid/mknetid.c b/libexec/mknetid/mknetid.c index 3a39b4b3e675..a5c8281ef34d 100644 --- a/libexec/mknetid/mknetid.c +++ b/libexec/mknetid/mknetid.c @@ -1,308 +1,303 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1995, 1996 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * netid map generator program * * Written by Bill Paul * Center for Telecommunications Research * Columbia University, New York City */ #include #include #include #include #include #include #include #include #include #include #include #include #include "hash.h" -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #define LINSIZ 1024 #define OPSYS "unix" /* Default location of group file. */ char *groupfile = _PATH_GROUP; /* Default location of master.passwd file. */ char *passfile = _PATH_PASSWD; /* Default location of hosts file. */ char *hostsfile = _PATH_HOSTS; /* Default location of netid file */ char *netidfile = "/etc/netid"; /* * Stored hash table of 'reverse' group member database * which we will construct. */ struct member_entry *mtable[TABLESIZE]; /* * Dupe table: used to keep track of entries so we don't * print the same thing twice. */ struct member_entry *dtable[TABLESIZE]; extern struct group *_getgrent(void); extern int _setgrent(void); extern void _endgrent(void); static void usage(void) { fprintf (stderr, "%s\n%s\n", "usage: mknetid [-q] [-g group_file] [-p passwd_file] [-h hosts_file]", " [-n netid_file] [-d domain]"); exit(1); } extern FILE *_gr_fp; int main(int argc, char *argv[]) { FILE *gfp, *pfp, *hfp, *nfp; char readbuf[LINSIZ]; char writebuf[LINSIZ]; struct group *gr; struct grouplist *glist; char *domain; int ch; gid_t i; char *ptr, *pidptr, *gidptr, *hptr; int quiet = 0; domain = NULL; while ((ch = getopt(argc, argv, "g:p:h:n:d:q")) != -1) { switch(ch) { case 'g': groupfile = optarg; break; case 'p': passfile = optarg; break; case 'h': hostsfile = optarg; break; case 'n': netidfile = optarg; break; case 'd': domain = optarg; break; case 'q': quiet++; break; default: usage(); break; } } if (domain == NULL) { if (yp_get_default_domain(&domain)) errx(1, "no domain name specified and default \ domain not set"); } if ((gfp = fopen(groupfile, "r")) == NULL) { err(1, "%s", groupfile); } if ((pfp = fopen(passfile, "r")) == NULL) { err(1, "%s", passfile); } if ((hfp = fopen(hostsfile, "r")) == NULL) { err(1, "%s", hostsfile); } if ((nfp = fopen(netidfile, "r")) == NULL) { /* netid is optional -- just continue */ nfp = NULL; } _gr_fp = gfp; /* Load all the group membership info into a hash table. */ _setgrent(); while((gr = _getgrent()) != NULL) { while(*gr->gr_mem) { mstore(mtable, *gr->gr_mem, gr->gr_gid, 0); gr->gr_mem++; } } fclose(gfp); _endgrent(); /* * Now parse the passwd database, spewing out the extra * group information we just stored if necessary. */ while(fgets(readbuf, LINSIZ, pfp)) { /* Ignore comments: ^[ \t]*# */ for (ptr = readbuf; *ptr != '\0'; ptr++) if (*ptr != ' ' && *ptr != '\t') break; if (*ptr == '#' || *ptr == '\0') continue; if ((ptr = strchr(readbuf, ':')) == NULL) { warnx("bad passwd file entry: %s", readbuf); continue; } *ptr = '\0'; ptr++; if ((ptr = strchr(ptr, ':')) == NULL) { warnx("bad passwd file entry: %s", readbuf); continue; } *ptr = '\0'; ptr++; pidptr = ptr; if ((ptr = strchr(ptr, ':')) == NULL) { warnx("bad passwd file entry: %s", readbuf); continue; } *ptr = '\0'; ptr++; gidptr = ptr; if ((ptr = strchr(ptr, ':')) == NULL) { warnx("bad passwd file entry: %s", readbuf); continue; } *ptr = '\0'; i = atol(gidptr); snprintf(writebuf, sizeof(writebuf), "%s.%s@%s", OPSYS, pidptr, domain); if (lookup(dtable, writebuf)) { if (!quiet) warnx("duplicate netid '%s.%s@%s' -- skipping", OPSYS, pidptr, domain); continue; } else { mstore(dtable, writebuf, 0, 1); } printf("%s.%s@%s %s:%s", OPSYS, pidptr, domain, pidptr, gidptr); if ((glist = lookup(mtable, (char *)&readbuf)) != NULL) { while(glist) { if (glist->groupid != i) printf(",%lu", (u_long)glist->groupid); glist = glist->next; } } printf ("\n"); } fclose(pfp); /* * Now parse the hosts database (this part sucks). */ while ((ptr = fgets(readbuf, LINSIZ, hfp))) { if (*ptr == '#') continue; if (!(hptr = strpbrk(ptr, "#\n"))) continue; *hptr = '\0'; if (!(hptr = strpbrk(ptr, " \t"))) continue; *hptr++ = '\0'; ptr = hptr; while (*ptr == ' ' || *ptr == '\t') ptr++; if (!(hptr = strpbrk(ptr, " \t"))) continue; *hptr++ = '\0'; snprintf(writebuf, sizeof(writebuf), "%s.%s@%s", OPSYS, ptr, domain); if (lookup(dtable, (char *)&writebuf)) { if (!quiet) warnx("duplicate netid '%s' -- skipping", writebuf); continue; } else { mstore(dtable, (char *)&writebuf, 0, 1); } printf ("%s.%s@%s 0:%s\n", OPSYS, ptr, domain, ptr); } fclose(hfp); /* * Lastly, copy out any extra information in the netid * file. If it's not open, just ignore it: it's optional anyway. */ if (nfp != NULL) { while(fgets(readbuf, LINSIZ, nfp)) { if (readbuf[0] == '#') continue; if ((ptr = strpbrk((char*)&readbuf, " \t")) == NULL) { warnx("bad netid entry: '%s'", readbuf); continue; } writebuf[0] = *ptr; *ptr = '\0'; if (lookup(dtable, (char *)&readbuf)) { if (!quiet) warnx("duplicate netid '%s' -- skipping", readbuf); continue; } else { mstore(dtable, (char *)&readbuf, 0, 1); } *ptr = writebuf[0]; printf("%s",readbuf); } fclose(nfp); } exit(0); } diff --git a/libexec/mknetid/parse_group.c b/libexec/mknetid/parse_group.c index e876bd9f2ff0..59ebf44eab37 100644 --- a/libexec/mknetid/parse_group.c +++ b/libexec/mknetid/parse_group.c @@ -1,159 +1,157 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static const char sccsid[] = "@(#)getgrent.c 8.2 (Berkeley) 3/21/94"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ /* * This is a slightly modified chunk of getgrent(3). All the YP support * and unneeded functions have been stripped out. */ #include #include #include #include #include FILE *_gr_fp; static struct group _gr_group; static int _gr_stayopen; static int grscan(int, int); static int start_gr(void); #define MAXGRP 200 static char *members[MAXGRP]; #define MAXLINELENGTH 1024 static char line[MAXLINELENGTH]; struct group * _getgrent(void) { if (!_gr_fp && !start_gr()) { return NULL; } if (!grscan(0, 0)) return(NULL); return(&_gr_group); } static int start_gr(void) { return 1; } int _setgroupent(int stayopen) { if (!start_gr()) return(0); _gr_stayopen = stayopen; return(1); } int _setgrent(void) { return(_setgroupent(0)); } void _endgrent(void) { if (_gr_fp) { (void)fclose(_gr_fp); _gr_fp = NULL; } } static int grscan(int search, int gid) { char *cp, **m; char *bp; for (;;) { if (!fgets(line, sizeof(line), _gr_fp)) return(0); bp = line; /* skip lines that are too big */ if (!strchr(line, '\n')) { int ch; while ((ch = getc(_gr_fp)) != '\n' && ch != EOF) ; continue; } if ((_gr_group.gr_name = strsep(&bp, ":\n")) == NULL) break; if (_gr_group.gr_name[0] == '+') continue; if ((_gr_group.gr_passwd = strsep(&bp, ":\n")) == NULL) break; if (!(cp = strsep(&bp, ":\n"))) continue; _gr_group.gr_gid = atoi(cp); if (search && _gr_group.gr_gid != gid) continue; cp = NULL; if (bp == NULL) /* !! Must check for this! */ break; for (m = _gr_group.gr_mem = members;; bp++) { if (m == &members[MAXGRP - 1]) break; if (*bp == ',') { if (cp) { *bp = '\0'; *m++ = cp; cp = NULL; } } else if (*bp == '\0' || *bp == '\n' || *bp == ' ') { if (cp) { *bp = '\0'; *m++ = cp; } break; } else if (cp == NULL) cp = bp; } *m = NULL; return(1); } /* NOTREACHED */ return (0); } diff --git a/libexec/rbootd/bpf.c b/libexec/rbootd/bpf.c index 0edcc06ccf7e..6fbe34111a67 100644 --- a/libexec/rbootd/bpf.c +++ b/libexec/rbootd/bpf.c @@ -1,408 +1,406 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1988, 1992 The University of Utah and the Center * for Software Science (CSS). * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * the Center for Software Science of the University of Utah Computer * Science Department. CSS requests users of this software to return * to css-dist@cs.utah.edu any improvements that they make and grant * CSS redistribution rights. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)bpf.c 8.1 (Berkeley) 6/4/93 * * From: Utah Hdr: bpf.c 3.1 92/07/06 * Author: Jeff Forys, University of Utah CSS */ #ifndef lint #if 0 static const char sccsid[] = "@(#)bpf.c 8.1 (Berkeley) 6/4/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "defs.h" #include "pathnames.h" static int BpfFd = -1; static unsigned BpfLen = 0; static u_int8_t *BpfPkt = NULL; /* ** BpfOpen -- Open and initialize a BPF device. ** ** Parameters: ** None. ** ** Returns: ** File descriptor of opened BPF device (for select() etc). ** ** Side Effects: ** If an error is encountered, the program terminates here. */ int BpfOpen(void) { struct ifreq ifr; char bpfdev[32]; int n = 0; /* * Open the first available BPF device. */ do { (void) sprintf(bpfdev, _PATH_BPF, n++); BpfFd = open(bpfdev, O_RDWR); } while (BpfFd < 0 && (errno == EBUSY || errno == EPERM)); if (BpfFd < 0) { syslog(LOG_ERR, "bpf: no available devices: %m"); Exit(0); } /* * Set interface name for bpf device, get data link layer * type and make sure it's type Ethernet. */ (void) strncpy(ifr.ifr_name, IntfName, sizeof(ifr.ifr_name)); if (ioctl(BpfFd, BIOCSETIF, (caddr_t)&ifr) < 0) { syslog(LOG_ERR, "bpf: ioctl(BIOCSETIF,%s): %m", IntfName); Exit(0); } /* * Make sure we are dealing with an Ethernet device. */ if (ioctl(BpfFd, BIOCGDLT, (caddr_t)&n) < 0) { syslog(LOG_ERR, "bpf: ioctl(BIOCGDLT): %m"); Exit(0); } if (n != DLT_EN10MB) { syslog(LOG_ERR,"bpf: %s: data-link type %d unsupported", IntfName, n); Exit(0); } /* * On read(), return packets immediately (do not buffer them). */ n = 1; if (ioctl(BpfFd, BIOCIMMEDIATE, (caddr_t)&n) < 0) { syslog(LOG_ERR, "bpf: ioctl(BIOCIMMEDIATE): %m"); Exit(0); } /* * Try to enable the chip/driver's multicast address filter to * grab our RMP address. If this fails, try promiscuous mode. * If this fails, there's no way we are going to get any RMP * packets so just exit here. */ #ifdef MSG_EOR ifr.ifr_addr.sa_len = RMP_ADDRLEN + 2; #endif ifr.ifr_addr.sa_family = AF_UNSPEC; memmove((char *)&ifr.ifr_addr.sa_data[0], &RmpMcastAddr[0], RMP_ADDRLEN); if (ioctl(BpfFd, BIOCPROMISC, (caddr_t)0) < 0) { syslog(LOG_ERR, "bpf: can't set promiscuous mode: %m"); Exit(0); } /* * Ask BPF how much buffer space it requires and allocate one. */ if (ioctl(BpfFd, BIOCGBLEN, (caddr_t)&BpfLen) < 0) { syslog(LOG_ERR, "bpf: ioctl(BIOCGBLEN): %m"); Exit(0); } if (BpfPkt == NULL) BpfPkt = (u_int8_t *)malloc(BpfLen); if (BpfPkt == NULL) { syslog(LOG_ERR, "bpf: out of memory (%u bytes for bpfpkt)", BpfLen); Exit(0); } /* * Write a little program to snarf RMP Boot packets and stuff * it down BPF's throat (i.e. set up the packet filter). */ { #define RMP ((struct rmp_packet *)0) static struct bpf_insn bpf_insn[] = { { BPF_LD|BPF_B|BPF_ABS, 0, 0, (long)&RMP->hp_llc.dsap }, { BPF_JMP|BPF_JEQ|BPF_K, 0, 5, IEEE_DSAP_HP }, { BPF_LD|BPF_H|BPF_ABS, 0, 0, (long)&RMP->hp_llc.cntrl }, { BPF_JMP|BPF_JEQ|BPF_K, 0, 3, IEEE_CNTL_HP }, { BPF_LD|BPF_H|BPF_ABS, 0, 0, (long)&RMP->hp_llc.dxsap }, { BPF_JMP|BPF_JEQ|BPF_K, 0, 1, HPEXT_DXSAP }, { BPF_RET|BPF_K, 0, 0, RMP_MAX_PACKET }, { BPF_RET|BPF_K, 0, 0, 0x0 } }; #undef RMP static struct bpf_program bpf_pgm = { sizeof(bpf_insn)/sizeof(bpf_insn[0]), bpf_insn }; if (ioctl(BpfFd, BIOCSETF, (caddr_t)&bpf_pgm) < 0) { syslog(LOG_ERR, "bpf: ioctl(BIOCSETF): %m"); Exit(0); } } return(BpfFd); } /* ** BPF GetIntfName -- Return the name of a network interface attached to ** the system, or 0 if none can be found. The interface ** must be configured up; the lowest unit number is ** preferred; loopback is ignored. ** ** Parameters: ** errmsg - if no network interface found, *errmsg explains why. ** ** Returns: ** A (static) pointer to interface name, or NULL on error. ** ** Side Effects: ** None. */ char * BpfGetIntfName(char **errmsg) { struct ifreq ibuf[8], *ifrp, *ifend, *mp; struct ifconf ifc; int fd; int minunit, n; char *cp; static char device[sizeof(ifrp->ifr_name)]; static char errbuf[128] = "No Error!"; if (errmsg != NULL) *errmsg = errbuf; if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { (void) strcpy(errbuf, "bpf: socket: %m"); return(NULL); } ifc.ifc_len = sizeof ibuf; ifc.ifc_buf = (caddr_t)ibuf; if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0 || ifc.ifc_len < sizeof(struct ifreq)) { (void) strcpy(errbuf, "bpf: ioctl(SIOCGIFCONF): %m"); return(NULL); } ifrp = ibuf; ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len); mp = NULL; minunit = 666; for (; ifrp < ifend; ++ifrp) { if (ioctl(fd, SIOCGIFFLAGS, (char *)ifrp) < 0) { (void) strcpy(errbuf, "bpf: ioctl(SIOCGIFFLAGS): %m"); return(NULL); } /* * If interface is down or this is the loopback interface, * ignore it. */ if ((ifrp->ifr_flags & IFF_UP) == 0 || #ifdef IFF_LOOPBACK (ifrp->ifr_flags & IFF_LOOPBACK)) #else (strcmp(ifrp->ifr_name, "lo0") == 0)) #endif continue; for (cp = ifrp->ifr_name; !isdigit(*cp); ++cp) ; n = atoi(cp); if (n < minunit) { minunit = n; mp = ifrp; } } (void) close(fd); if (mp == NULL) { (void) strcpy(errbuf, "bpf: no interfaces found"); return(NULL); } (void) strcpy(device, mp->ifr_name); return(device); } /* ** BpfRead -- Read packets from a BPF device and fill in `rconn'. ** ** Parameters: ** rconn - filled in with next packet. ** doread - is True if we can issue a read() syscall. ** ** Returns: ** True if `rconn' contains a new packet, False otherwise. ** ** Side Effects: ** None. */ int BpfRead(RMPCONN *rconn, int doread) { int datlen, caplen, hdrlen; static u_int8_t *bp = NULL, *ep = NULL; int cc; /* * The read() may block, or it may return one or more packets. * We let the caller decide whether or not we can issue a read(). */ if (doread) { if ((cc = read(BpfFd, (char *)BpfPkt, (int)BpfLen)) < 0) { syslog(LOG_ERR, "bpf: read: %m"); return(0); } else { bp = BpfPkt; ep = BpfPkt + cc; } } #define bhp ((struct bpf_hdr *)bp) /* * If there is a new packet in the buffer, stuff it into `rconn' * and return a success indication. */ if (bp < ep) { datlen = bhp->bh_datalen; caplen = bhp->bh_caplen; hdrlen = bhp->bh_hdrlen; if (caplen != datlen) syslog(LOG_ERR, "bpf: short packet dropped (%d of %d bytes)", caplen, datlen); else if (caplen > sizeof(struct rmp_packet)) syslog(LOG_ERR, "bpf: large packet dropped (%d bytes)", caplen); else { rconn->rmplen = caplen; memmove((char *)&rconn->tstamp, (char *)&bhp->bh_tstamp, sizeof(struct timeval)); memmove((char *)&rconn->rmp, (char *)bp + hdrlen, caplen); } bp += BPF_WORDALIGN(caplen + hdrlen); return(1); } #undef bhp return(0); } /* ** BpfWrite -- Write packet to BPF device. ** ** Parameters: ** rconn - packet to send. ** ** Returns: ** True if write succeeded, False otherwise. ** ** Side Effects: ** None. */ int BpfWrite(RMPCONN *rconn) { if (write(BpfFd, (char *)&rconn->rmp, rconn->rmplen) < 0) { syslog(LOG_ERR, "write: %s: %m", EnetStr(rconn)); return(0); } return(1); } /* ** BpfClose -- Close a BPF device. ** ** Parameters: ** None. ** ** Returns: ** Nothing. ** ** Side Effects: ** None. */ void BpfClose(void) { struct ifreq ifr; if (BpfPkt != NULL) { free((char *)BpfPkt); BpfPkt = NULL; } if (BpfFd == -1) return; #ifdef MSG_EOR ifr.ifr_addr.sa_len = RMP_ADDRLEN + 2; #endif ifr.ifr_addr.sa_family = AF_UNSPEC; memmove((char *)&ifr.ifr_addr.sa_data[0], &RmpMcastAddr[0], RMP_ADDRLEN); if (ioctl(BpfFd, SIOCDELMULTI, (caddr_t)&ifr) < 0) (void) ioctl(BpfFd, BIOCPROMISC, (caddr_t)0); (void) close(BpfFd); BpfFd = -1; } diff --git a/libexec/rbootd/conf.c b/libexec/rbootd/conf.c index af46f53c3c88..a48d3efdb9c1 100644 --- a/libexec/rbootd/conf.c +++ b/libexec/rbootd/conf.c @@ -1,91 +1,89 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1988, 1992 The University of Utah and the Center * for Software Science (CSS). * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * the Center for Software Science of the University of Utah Computer * Science Department. CSS requests users of this software to return * to css-dist@cs.utah.edu any improvements that they make and grant * CSS redistribution rights. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)conf.c 8.1 (Berkeley) 6/4/93 * * From: Utah Hdr: conf.c 3.1 92/07/06 * Author: Jeff Forys, University of Utah CSS */ #ifndef lint #if 0 static const char sccsid[] = "@(#)conf.c 8.1 (Berkeley) 6/4/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include "defs.h" #include "pathnames.h" /* ** Define (and possibly initialize) global variables here. ** ** Caveat: ** The maximum number of bootable files (`char *BootFiles[]') is ** limited to C_MAXFILE (i.e. the maximum number of files that ** can be spec'd in the configuration file). This was done to ** simplify the boot file search code. */ char MyHost[MAXHOSTNAMELEN]; /* host name */ pid_t MyPid; /* process id */ int DebugFlg = 0; /* set true if debugging */ int BootAny = 0; /* set true if we boot anyone */ char *ConfigFile = NULL; /* configuration file */ char *DfltConfig = _PATH_RBOOTDCONF; /* default configuration file */ char *PidFile = _PATH_RBOOTDPID; /* file w/pid of server */ char *BootDir = _PATH_RBOOTDLIB; /* directory w/boot files */ char *DbgFile = _PATH_RBOOTDDBG; /* debug output file */ FILE *DbgFp = NULL; /* debug file pointer */ char *IntfName = NULL; /* intf we are attached to */ u_int16_t SessionID = 0; /* generated session ID */ char *BootFiles[C_MAXFILE]; /* list of boot files */ CLIENT *Clients = NULL; /* list of addrs we'll accept */ RMPCONN *RmpConns = NULL; /* list of active connections */ u_int8_t RmpMcastAddr[RMP_ADDRLEN] = RMP_ADDR; /* RMP multicast address */ diff --git a/libexec/rbootd/parseconf.c b/libexec/rbootd/parseconf.c index 9585e87185c6..262f81fb68d7 100644 --- a/libexec/rbootd/parseconf.c +++ b/libexec/rbootd/parseconf.c @@ -1,360 +1,358 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1988, 1992 The University of Utah and the Center * for Software Science (CSS). * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * the Center for Software Science of the University of Utah Computer * Science Department. CSS requests users of this software to return * to css-dist@cs.utah.edu any improvements that they make and grant * CSS redistribution rights. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)parseconf.c 8.1 (Berkeley) 6/4/93 * * From: Utah Hdr: parseconf.c 3.1 92/07/06 * Author: Jeff Forys, University of Utah CSS */ #ifndef lint #if 0 static const char sccsid[] = "@(#)parseconf.c 8.1 (Berkeley) 6/4/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include "defs.h" /* ** ParseConfig -- parse the config file into linked list of clients. ** ** Parameters: ** None. ** ** Returns: ** 1 on success, 0 otherwise. ** ** Side Effects: ** - Linked list of clients will be (re)allocated. ** ** Warnings: ** - GetBootFiles() must be called before this routine ** to create a linked list of default boot files. */ int ParseConfig(void) { FILE *fp; CLIENT *client; u_int8_t *addr; char line[C_LINELEN]; char *cp, *bcp; int i, j; int omask, linecnt = 0; if (BootAny) /* ignore config file */ return(1); FreeClients(); /* delete old list of clients */ if ((fp = fopen(ConfigFile, "r")) == NULL) { syslog(LOG_ERR, "ParseConfig: can't open config file (%s)", ConfigFile); return(0); } /* * We've got to block SIGHUP to prevent reconfiguration while * dealing with the linked list of Clients. This can be done * when actually linking the new client into the list, but * this could have unexpected results if the server was HUP'd * whilst reconfiguring. Hence, it is done here. */ omask = sigblock(sigmask(SIGHUP)); /* * GETSTR positions `bcp' at the start of the current token, * and null terminates it. `cp' is positioned at the start * of the next token. spaces & commas are separators. */ #define GETSTR while (isspace(*cp) || *cp == ',') cp++; \ bcp = cp; \ while (*cp && *cp!=',' && !isspace(*cp)) cp++; \ if (*cp) *cp++ = '\0' /* * For each line, parse it into a new CLIENT struct. */ while (fgets(line, C_LINELEN, fp) != NULL) { linecnt++; /* line counter */ if (*line == '\0' || *line == '#') /* ignore comment */ continue; if ((cp = strchr(line,'#')) != NULL) /* trash comments */ *cp = '\0'; cp = line; /* init `cp' */ GETSTR; /* get RMP addr */ if (bcp == cp) /* all delimiters */ continue; /* * Get an RMP address from a string. Abort on failure. */ if ((addr = ParseAddr(bcp)) == NULL) { syslog(LOG_ERR, "ParseConfig: line %d: can't parse <%s>", linecnt, bcp); continue; } if ((client = NewClient(addr)) == NULL) /* alloc new client */ continue; GETSTR; /* get first file */ /* * If no boot files are spec'd, use the default list. * Otherwise, validate each file (`bcp') against the * list of boot-able files. */ i = 0; if (bcp == cp) /* no files spec'd */ for (; i < C_MAXFILE && BootFiles[i] != NULL; i++) client->files[i] = BootFiles[i]; else { do { /* * For each boot file spec'd, make sure it's * in our list. If so, include a pointer to * it in the CLIENT's list of boot files. */ for (j = 0; ; j++) { if (j==C_MAXFILE||BootFiles[j]==NULL) { syslog(LOG_ERR, "ParseConfig: line %d: no boot file (%s)", linecnt, bcp); break; } if (STREQN(BootFiles[j], bcp)) { if (i < C_MAXFILE) client->files[i++] = BootFiles[j]; else syslog(LOG_ERR, "ParseConfig: line %d: too many boot files (%s)", linecnt, bcp); break; } } GETSTR; /* get next file */ } while (bcp != cp); /* * Restricted list of boot files were spec'd, * however, none of them were found. Since we * apparently can't let them boot "just anything", * the entire record is invalidated. */ if (i == 0) { FreeClient(client); continue; } } /* * Link this client into the linked list of clients. * SIGHUP has already been blocked. */ if (Clients) client->next = Clients; Clients = client; } (void) fclose(fp); /* close config file */ (void) sigsetmask(omask); /* reset signal mask */ return(1); /* return success */ } /* ** ParseAddr -- Parse a string containing an RMP address. ** ** This routine is fairly liberal at parsing an RMP address. The ** address must contain 6 octets consisting of between 0 and 2 hex ** chars (upper/lower case) separated by colons. If two colons are ** together (e.g. "::", the octet between them is recorded as being ** zero. Hence, the following addrs are all valid and parse to the ** same thing: ** ** 08:00:09:00:66:ad 8::9:0:66:AD 8::9::66:aD ** ** For clarity, an RMP address is really an Ethernet address, but ** since the HP boot code uses IEEE 802.3, it's really an IEEE ** 802.3 address. Of course, all of these are identical. ** ** Parameters: ** str - string representation of an RMP address. ** ** Returns: ** pointer to a static array of RMP_ADDRLEN bytes. ** ** Side Effects: ** None. ** ** Warnings: ** - The return value points to a static buffer; it must ** be copied if it's to be saved. */ u_int8_t * ParseAddr(char *str) { static u_int8_t addr[RMP_ADDRLEN]; char *cp; unsigned i; int part, subpart; memset((char *)&addr[0], 0, RMP_ADDRLEN); /* zero static buffer */ part = subpart = 0; for (cp = str; *cp; cp++) { /* * A colon (`:') must be used to delimit each octet. */ if (*cp == ':') { if (++part == RMP_ADDRLEN) /* too many parts */ return(NULL); subpart = 0; continue; } /* * Convert hex character to an integer. */ if (isdigit(*cp)) i = *cp - '0'; else { i = (isupper(*cp)? tolower(*cp): *cp) - 'a' + 10; if (i < 10 || i > 15) /* not a hex char */ return(NULL); } if (subpart++) { if (subpart > 2) /* too many hex chars */ return(NULL); addr[part] <<= 4; } addr[part] |= i; } if (part != (RMP_ADDRLEN-1)) /* too few parts */ return(NULL); return(&addr[0]); } /* ** GetBootFiles -- record list of files in current (boot) directory. ** ** Parameters: ** None. ** ** Returns: ** Number of boot files on success, 0 on failure. ** ** Side Effects: ** Strings in `BootFiles' are freed/allocated. ** ** Warnings: ** - After this routine is called, ParseConfig() must be ** called to re-order it's list of boot file pointers. */ int GetBootFiles(void) { DIR *dfd; struct stat statb; struct dirent *dp; int i; /* * Free the current list of boot files. */ for (i = 0; i < C_MAXFILE && BootFiles[i] != NULL; i++) { FreeStr(BootFiles[i]); BootFiles[i] = NULL; } /* * Open current directory to read boot file names. */ if ((dfd = opendir(".")) == NULL) { /* open BootDir */ syslog(LOG_ERR, "GetBootFiles: can't open directory (%s)\n", BootDir); return(0); } /* * Read each boot file name and allocate space for it in the * list of boot files (BootFiles). All boot files read after * C_MAXFILE will be ignored. */ i = 0; for (dp = readdir(dfd); dp != NULL; dp = readdir(dfd)) { if (stat(dp->d_name, &statb) < 0 || (statb.st_mode & S_IFMT) != S_IFREG) continue; if (i == C_MAXFILE) syslog(LOG_ERR, "GetBootFiles: too many boot files (%s ignored)", dp->d_name); else if ((BootFiles[i] = NewStr(dp->d_name)) != NULL) i++; } (void) closedir(dfd); /* close BootDir */ if (i == 0) /* can't find any boot files */ syslog(LOG_ERR, "GetBootFiles: no boot files (%s)\n", BootDir); return(i); } diff --git a/libexec/rbootd/rmpproto.c b/libexec/rbootd/rmpproto.c index a31ec1de8abf..2603e697d8b8 100644 --- a/libexec/rbootd/rmpproto.c +++ b/libexec/rbootd/rmpproto.c @@ -1,586 +1,584 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1988, 1992 The University of Utah and the Center * for Software Science (CSS). * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * the Center for Software Science of the University of Utah Computer * Science Department. CSS requests users of this software to return * to css-dist@cs.utah.edu any improvements that they make and grant * CSS redistribution rights. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)rmpproto.c 8.1 (Berkeley) 6/4/93 * * From: Utah Hdr: rmpproto.c 3.1 92/07/06 * Author: Jeff Forys, University of Utah CSS */ #ifndef lint #if 0 static const char sccsid[] = "@(#)rmpproto.c 8.1 (Berkeley) 6/4/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include "defs.h" /* ** ProcessPacket -- determine packet type and do what's required. ** ** An RMP BOOT packet has been received. Look at the type field ** and process Boot Requests, Read Requests, and Boot Complete ** packets. Any other type will be dropped with a warning msg. ** ** Parameters: ** rconn - the new connection ** client - list of files available to this host ** ** Returns: ** Nothing. ** ** Side Effects: ** - If this is a valid boot request, it will be added to ** the linked list of outstanding requests (RmpConns). ** - If this is a valid boot complete, its associated ** entry in RmpConns will be deleted. ** - Also, unless we run out of memory, a reply will be ** sent to the host that sent the packet. */ void ProcessPacket(RMPCONN *rconn, CLIENT *client) { struct rmp_packet *rmp; RMPCONN *rconnout; rmp = &rconn->rmp; /* cache pointer to RMP packet */ switch(rmp->r_type) { /* do what we came here to do */ case RMP_BOOT_REQ: /* boot request */ if ((rconnout = NewConn(rconn)) == NULL) return; /* * If the Session ID is 0xffff, this is a "probe" * packet and we do not want to add the connection * to the linked list of active connections. There * are two types of probe packets, if the Sequence * Number is 0 they want to know our host name, o/w * they want the name of the file associated with * the number spec'd by the Sequence Number. * * If this is an actual boot request, open the file * and send a reply. If SendBootRepl() does not * return 0, add the connection to the linked list * of active connections, otherwise delete it since * an error was encountered. */ if (ntohs(rmp->r_brq.rmp_session) == RMP_PROBESID) { if (WORDZE(rmp->r_brq.rmp_seqno)) (void) SendServerID(rconnout); else (void) SendFileNo(rmp, rconnout, client? client->files: BootFiles); FreeConn(rconnout); } else { if (SendBootRepl(rmp, rconnout, client? client->files: BootFiles)) AddConn(rconnout); else FreeConn(rconnout); } break; case RMP_BOOT_REPL: /* boot reply (not valid) */ syslog(LOG_WARNING, "%s: sent a boot reply", EnetStr(rconn)); break; case RMP_READ_REQ: /* read request */ /* * Send a portion of the boot file. */ (void) SendReadRepl(rconn); break; case RMP_READ_REPL: /* read reply (not valid) */ syslog(LOG_WARNING, "%s: sent a read reply", EnetStr(rconn)); break; case RMP_BOOT_DONE: /* boot complete */ /* * Remove the entry from the linked list of active * connections. */ (void) BootDone(rconn); break; default: /* unknown RMP packet type */ syslog(LOG_WARNING, "%s: unknown packet type (%u)", EnetStr(rconn), rmp->r_type); } } /* ** SendServerID -- send our host name to who ever requested it. ** ** Parameters: ** rconn - the reply packet to be formatted. ** ** Returns: ** 1 on success, 0 on failure. ** ** Side Effects: ** none. */ int SendServerID(RMPCONN *rconn) { struct rmp_packet *rpl; char *src, *dst; u_int8_t *size; rpl = &rconn->rmp; /* cache ptr to RMP packet */ /* * Set up assorted fields in reply packet. */ rpl->r_brpl.rmp_type = RMP_BOOT_REPL; rpl->r_brpl.rmp_retcode = RMP_E_OKAY; ZEROWORD(rpl->r_brpl.rmp_seqno); rpl->r_brpl.rmp_session = 0; rpl->r_brpl.rmp_version = htons(RMP_VERSION); size = &rpl->r_brpl.rmp_flnmsize; /* ptr to length of host name */ /* * Copy our host name into the reply packet incrementing the * length as we go. Stop at RMP_HOSTLEN or the first dot. */ src = MyHost; dst = (char *) &rpl->r_brpl.rmp_flnm; for (*size = 0; *size < RMP_HOSTLEN; (*size)++) { if (*src == '.' || *src == '\0') break; *dst++ = *src++; } rconn->rmplen = RMPBOOTSIZE(*size); /* set packet length */ return(SendPacket(rconn)); /* send packet */ } /* ** SendFileNo -- send the name of a bootable file to the requester. ** ** Parameters: ** req - RMP BOOT packet containing the request. ** rconn - the reply packet to be formatted. ** filelist - list of files available to the requester. ** ** Returns: ** 1 on success, 0 on failure. ** ** Side Effects: ** none. */ int SendFileNo(struct rmp_packet *req, RMPCONN *rconn, char *filelist[]) { struct rmp_packet *rpl; char *src, *dst; u_int8_t *size; int i; GETWORD(req->r_brpl.rmp_seqno, i); /* SeqNo is really FileNo */ rpl = &rconn->rmp; /* cache ptr to RMP packet */ /* * Set up assorted fields in reply packet. */ rpl->r_brpl.rmp_type = RMP_BOOT_REPL; PUTWORD(i, rpl->r_brpl.rmp_seqno); i--; rpl->r_brpl.rmp_session = 0; rpl->r_brpl.rmp_version = htons(RMP_VERSION); size = &rpl->r_brpl.rmp_flnmsize; /* ptr to length of filename */ *size = 0; /* init length to zero */ /* * Copy the file name into the reply packet incrementing the * length as we go. Stop at end of string or when RMPBOOTDATA * characters have been copied. Also, set return code to * indicate success or "no more files". */ if (i < C_MAXFILE && filelist[i] != NULL) { src = filelist[i]; dst = (char *)&rpl->r_brpl.rmp_flnm; for (; *src && *size < RMPBOOTDATA; (*size)++) { if (*src == '\0') break; *dst++ = *src++; } rpl->r_brpl.rmp_retcode = RMP_E_OKAY; } else rpl->r_brpl.rmp_retcode = RMP_E_NODFLT; rconn->rmplen = RMPBOOTSIZE(*size); /* set packet length */ return(SendPacket(rconn)); /* send packet */ } /* ** SendBootRepl -- open boot file and respond to boot request. ** ** Parameters: ** req - RMP BOOT packet containing the request. ** rconn - the reply packet to be formatted. ** filelist - list of files available to the requester. ** ** Returns: ** 1 on success, 0 on failure. ** ** Side Effects: ** none. */ int SendBootRepl(struct rmp_packet *req, RMPCONN *rconn, char *filelist[]) { int retval; char *filename, filepath[RMPBOOTDATA+1]; RMPCONN *oldconn; struct rmp_packet *rpl; char *src, *dst1, *dst2; u_int8_t i; /* * If another connection already exists, delete it since we * are obviously starting again. */ if ((oldconn = FindConn(rconn)) != NULL) { syslog(LOG_WARNING, "%s: dropping existing connection", EnetStr(oldconn)); RemoveConn(oldconn); } rpl = &rconn->rmp; /* cache ptr to RMP packet */ /* * Set up assorted fields in reply packet. */ rpl->r_brpl.rmp_type = RMP_BOOT_REPL; COPYWORD(req->r_brq.rmp_seqno, rpl->r_brpl.rmp_seqno); rpl->r_brpl.rmp_session = htons(GenSessID()); rpl->r_brpl.rmp_version = htons(RMP_VERSION); rpl->r_brpl.rmp_flnmsize = req->r_brq.rmp_flnmsize; /* * Copy file name to `filepath' string, and into reply packet. */ src = &req->r_brq.rmp_flnm; dst1 = filepath; dst2 = &rpl->r_brpl.rmp_flnm; for (i = 0; i < req->r_brq.rmp_flnmsize; i++) *dst1++ = *dst2++ = *src++; *dst1 = '\0'; /* * If we are booting HP-UX machines, their secondary loader will * ask for files like "/hp-ux". As a security measure, we do not * allow boot files to lay outside the boot directory (unless they * are purposely link'd out. So, make `filename' become the path- * stripped file name and spoof the client into thinking that it * really got what it wanted. */ filename = strrchr(filepath,'/'); filename = filename? filename + 1: filepath; /* * Check that this is a valid boot file name. */ for (i = 0; i < C_MAXFILE && filelist[i] != NULL; i++) if (STREQN(filename, filelist[i])) goto match; /* * Invalid boot file name, set error and send reply packet. */ rpl->r_brpl.rmp_retcode = RMP_E_NOFILE; retval = 0; goto sendpkt; match: /* * This is a valid boot file. Open the file and save the file * descriptor associated with this connection and set success * indication. If the file couldnt be opened, set error: * "no such file or dir" - RMP_E_NOFILE * "file table overflow" - RMP_E_BUSY * "too many open files" - RMP_E_BUSY * anything else - RMP_E_OPENFILE */ if ((rconn->bootfd = open(filename, O_RDONLY, 0600)) < 0) { rpl->r_brpl.rmp_retcode = (errno == ENOENT)? RMP_E_NOFILE: (errno == EMFILE || errno == ENFILE)? RMP_E_BUSY: RMP_E_OPENFILE; retval = 0; } else { rpl->r_brpl.rmp_retcode = RMP_E_OKAY; retval = 1; } sendpkt: syslog(LOG_INFO, "%s: request to boot %s (%s)", EnetStr(rconn), filename, retval? "granted": "denied"); rconn->rmplen = RMPBOOTSIZE(rpl->r_brpl.rmp_flnmsize); return (retval & SendPacket(rconn)); } /* ** SendReadRepl -- send a portion of the boot file to the requester. ** ** Parameters: ** rconn - the reply packet to be formatted. ** ** Returns: ** 1 on success, 0 on failure. ** ** Side Effects: ** none. */ int SendReadRepl(RMPCONN *rconn) { int retval = 0; RMPCONN *oldconn; struct rmp_packet *rpl, *req; int size = 0; int madeconn = 0; /* * Find the old connection. If one doesn't exist, create one only * to return the error code. */ if ((oldconn = FindConn(rconn)) == NULL) { if ((oldconn = NewConn(rconn)) == NULL) return(0); syslog(LOG_ERR, "SendReadRepl: no active connection (%s)", EnetStr(rconn)); madeconn++; } req = &rconn->rmp; /* cache ptr to request packet */ rpl = &oldconn->rmp; /* cache ptr to reply packet */ if (madeconn) { /* no active connection above; abort */ rpl->r_rrpl.rmp_retcode = RMP_E_ABORT; retval = 1; goto sendpkt; } /* * Make sure Session ID's match. */ if (ntohs(req->r_rrq.rmp_session) != ((rpl->r_type == RMP_BOOT_REPL)? ntohs(rpl->r_brpl.rmp_session): ntohs(rpl->r_rrpl.rmp_session))) { syslog(LOG_ERR, "SendReadRepl: bad session id (%s)", EnetStr(rconn)); rpl->r_rrpl.rmp_retcode = RMP_E_BADSID; retval = 1; goto sendpkt; } /* * If the requester asks for more data than we can fit, * silently clamp the request size down to RMPREADDATA. * * N.B. I do not know if this is "legal", however it seems * to work. This is necessary for bpfwrite() on machines * with MCLBYTES less than 1514. */ if (ntohs(req->r_rrq.rmp_size) > RMPREADDATA) req->r_rrq.rmp_size = htons(RMPREADDATA); /* * Position read head on file according to info in request packet. */ GETWORD(req->r_rrq.rmp_offset, size); if (lseek(oldconn->bootfd, (off_t)size, SEEK_SET) < 0) { syslog(LOG_ERR, "SendReadRepl: lseek: %m (%s)", EnetStr(rconn)); rpl->r_rrpl.rmp_retcode = RMP_E_ABORT; retval = 1; goto sendpkt; } /* * Read data directly into reply packet. */ if ((size = read(oldconn->bootfd, &rpl->r_rrpl.rmp_data, (int) ntohs(req->r_rrq.rmp_size))) <= 0) { if (size < 0) { syslog(LOG_ERR, "SendReadRepl: read: %m (%s)", EnetStr(rconn)); rpl->r_rrpl.rmp_retcode = RMP_E_ABORT; } else { rpl->r_rrpl.rmp_retcode = RMP_E_EOF; } retval = 1; goto sendpkt; } /* * Set success indication. */ rpl->r_rrpl.rmp_retcode = RMP_E_OKAY; sendpkt: /* * Set up assorted fields in reply packet. */ rpl->r_rrpl.rmp_type = RMP_READ_REPL; COPYWORD(req->r_rrq.rmp_offset, rpl->r_rrpl.rmp_offset); rpl->r_rrpl.rmp_session = req->r_rrq.rmp_session; oldconn->rmplen = RMPREADSIZE(size); /* set size of packet */ retval &= SendPacket(oldconn); /* send packet */ if (madeconn) /* clean up after ourself */ FreeConn(oldconn); return (retval); } /* ** BootDone -- free up memory allocated for a connection. ** ** Parameters: ** rconn - incoming boot complete packet. ** ** Returns: ** 1 on success, 0 on failure. ** ** Side Effects: ** none. */ int BootDone(RMPCONN *rconn) { RMPCONN *oldconn; struct rmp_packet *rpl; /* * If we can't find the connection, ignore the request. */ if ((oldconn = FindConn(rconn)) == NULL) { syslog(LOG_ERR, "BootDone: no existing connection (%s)", EnetStr(rconn)); return(0); } rpl = &oldconn->rmp; /* cache ptr to RMP packet */ /* * Make sure Session ID's match. */ if (ntohs(rconn->rmp.r_rrq.rmp_session) != ((rpl->r_type == RMP_BOOT_REPL)? ntohs(rpl->r_brpl.rmp_session): ntohs(rpl->r_rrpl.rmp_session))) { syslog(LOG_ERR, "BootDone: bad session id (%s)", EnetStr(rconn)); return(0); } RemoveConn(oldconn); /* remove connection */ syslog(LOG_INFO, "%s: boot complete", EnetStr(rconn)); return(1); } /* ** SendPacket -- send an RMP packet to a remote host. ** ** Parameters: ** rconn - packet to be sent. ** ** Returns: ** 1 on success, 0 on failure. ** ** Side Effects: ** none. */ int SendPacket(RMPCONN *rconn) { /* * Set Ethernet Destination address to Source (BPF and the enet * driver will take care of getting our source address set). */ memmove((char *)&rconn->rmp.hp_hdr.daddr[0], (char *)&rconn->rmp.hp_hdr.saddr[0], RMP_ADDRLEN); rconn->rmp.hp_hdr.len = htons(rconn->rmplen - sizeof(struct hp_hdr)); /* * Reverse 802.2/HP Extended Source & Destination Access Pts. */ rconn->rmp.hp_llc.dxsap = htons(HPEXT_SXSAP); rconn->rmp.hp_llc.sxsap = htons(HPEXT_DXSAP); /* * Last time this connection was active. */ (void)gettimeofday(&rconn->tstamp, NULL); if (DbgFp != NULL) /* display packet */ DispPkt(rconn,DIR_SENT); /* * Send RMP packet to remote host. */ return(BpfWrite(rconn)); } diff --git a/libexec/rbootd/utils.c b/libexec/rbootd/utils.c index f5c2d62d30d7..36a7116a05c4 100644 --- a/libexec/rbootd/utils.c +++ b/libexec/rbootd/utils.c @@ -1,546 +1,544 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1988, 1992 The University of Utah and the Center * for Software Science (CSS). * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * the Center for Software Science of the University of Utah Computer * Science Department. CSS requests users of this software to return * to css-dist@cs.utah.edu any improvements that they make and grant * CSS redistribution rights. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)utils.c 8.1 (Berkeley) 6/4/93 * * From: Utah Hdr: utils.c 3.1 92/07/06 * Author: Jeff Forys, University of Utah CSS */ #ifndef lint #if 0 static const char sccsid[] = "@(#)utils.c 8.1 (Berkeley) 6/4/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include "defs.h" /* ** DispPkt -- Display the contents of an RMPCONN packet. ** ** Parameters: ** rconn - packet to be displayed. ** direct - direction packet is going (DIR_*). ** ** Returns: ** Nothing. ** ** Side Effects: ** None. */ void DispPkt(RMPCONN *rconn, int direct) { static const char BootFmt[] = "\t\tRetCode:%u SeqNo:%x SessID:%x Vers:%u"; static const char ReadFmt[] = "\t\tRetCode:%u Offset:%x SessID:%x\n"; struct tm *tmp; struct rmp_packet *rmp; int i, omask; u_int32_t t; /* * Since we will be working with RmpConns as well as DbgFp, we * must block signals that can affect either. */ omask = sigblock(sigmask(SIGHUP)|sigmask(SIGUSR1)|sigmask(SIGUSR2)); if (DbgFp == NULL) { /* sanity */ (void) sigsetmask(omask); return; } /* display direction packet is going using '>>>' or '<<<' */ fputs((direct==DIR_RCVD)?"<<< ":(direct==DIR_SENT)?">>> ":"", DbgFp); /* display packet timestamp */ tmp = localtime((time_t *)&rconn->tstamp.tv_sec); fprintf(DbgFp, "%02d:%02d:%02d.%06ld ", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, rconn->tstamp.tv_usec); /* display src or dst addr and information about network interface */ fprintf(DbgFp, "Addr: %s Intf: %s\n", EnetStr(rconn), IntfName); rmp = &rconn->rmp; /* display IEEE 802.2 Logical Link Control header */ (void) fprintf(DbgFp, "\t802.2 LLC: DSAP:%x SSAP:%x CTRL:%x\n", rmp->hp_llc.dsap, rmp->hp_llc.ssap, ntohs(rmp->hp_llc.cntrl)); /* display HP extensions to 802.2 Logical Link Control header */ (void) fprintf(DbgFp, "\tHP Ext: DXSAP:%x SXSAP:%x\n", ntohs(rmp->hp_llc.dxsap), ntohs(rmp->hp_llc.sxsap)); /* * Display information about RMP packet using type field to * determine what kind of packet this is. */ switch(rmp->r_type) { case RMP_BOOT_REQ: /* boot request */ (void) fprintf(DbgFp, "\tBoot Request:"); GETWORD(rmp->r_brq.rmp_seqno, t); if (ntohs(rmp->r_brq.rmp_session) == RMP_PROBESID) { if (WORDZE(rmp->r_brq.rmp_seqno)) fputs(" (Send Server ID)", DbgFp); else fprintf(DbgFp," (Send Filename #%u)",t); } (void) fputc('\n', DbgFp); (void) fprintf(DbgFp, BootFmt, rmp->r_brq.rmp_retcode, t, ntohs(rmp->r_brq.rmp_session), ntohs(rmp->r_brq.rmp_version)); (void) fprintf(DbgFp, "\n\t\tMachine Type: "); for (i = 0; i < RMP_MACHLEN; i++) (void) fputc(rmp->r_brq.rmp_machtype[i], DbgFp); DspFlnm(rmp->r_brq.rmp_flnmsize, &rmp->r_brq.rmp_flnm); break; case RMP_BOOT_REPL: /* boot reply */ fprintf(DbgFp, "\tBoot Reply:\n"); GETWORD(rmp->r_brpl.rmp_seqno, t); (void) fprintf(DbgFp, BootFmt, rmp->r_brpl.rmp_retcode, t, ntohs(rmp->r_brpl.rmp_session), ntohs(rmp->r_brpl.rmp_version)); DspFlnm(rmp->r_brpl.rmp_flnmsize,&rmp->r_brpl.rmp_flnm); break; case RMP_READ_REQ: /* read request */ (void) fprintf(DbgFp, "\tRead Request:\n"); GETWORD(rmp->r_rrq.rmp_offset, t); (void) fprintf(DbgFp, ReadFmt, rmp->r_rrq.rmp_retcode, t, ntohs(rmp->r_rrq.rmp_session)); (void) fprintf(DbgFp, "\t\tNoOfBytes: %u\n", ntohs(rmp->r_rrq.rmp_size)); break; case RMP_READ_REPL: /* read reply */ (void) fprintf(DbgFp, "\tRead Reply:\n"); GETWORD(rmp->r_rrpl.rmp_offset, t); (void) fprintf(DbgFp, ReadFmt, rmp->r_rrpl.rmp_retcode, t, ntohs(rmp->r_rrpl.rmp_session)); (void) fprintf(DbgFp, "\t\tNoOfBytesSent: %zu\n", rconn->rmplen - RMPREADSIZE(0)); break; case RMP_BOOT_DONE: /* boot complete */ (void) fprintf(DbgFp, "\tBoot Complete:\n"); (void) fprintf(DbgFp, "\t\tRetCode:%u SessID:%x\n", rmp->r_done.rmp_retcode, ntohs(rmp->r_done.rmp_session)); break; default: /* ??? */ (void) fprintf(DbgFp, "\tUnknown Type:(%d)\n", rmp->r_type); } (void) fputc('\n', DbgFp); (void) fflush(DbgFp); (void) sigsetmask(omask); /* reset old signal mask */ } /* ** GetEtherAddr -- convert an RMP (Ethernet) address into a string. ** ** An RMP BOOT packet has been received. Look at the type field ** and process Boot Requests, Read Requests, and Boot Complete ** packets. Any other type will be dropped with a warning msg. ** ** Parameters: ** addr - array of RMP_ADDRLEN bytes. ** ** Returns: ** Pointer to static string representation of `addr'. ** ** Side Effects: ** None. ** ** Warnings: ** - The return value points to a static buffer; it must ** be copied if it's to be saved. */ char * GetEtherAddr(u_int8_t *addr) { static char Hex[] = "0123456789abcdef"; static char etherstr[RMP_ADDRLEN*3]; int i; char *cp; /* * For each byte in `addr', convert it to ":". * The last byte does not get a trailing `:' appended. */ i = 0; cp = etherstr; for(;;) { *cp++ = Hex[*addr >> 4 & 0xf]; *cp++ = Hex[*addr++ & 0xf]; if (++i == RMP_ADDRLEN) break; *cp++ = ':'; } *cp = '\0'; return(etherstr); } /* ** DispFlnm -- Print a string of bytes to DbgFp (often, a file name). ** ** Parameters: ** size - number of bytes to print. ** flnm - address of first byte. ** ** Returns: ** Nothing. ** ** Side Effects: ** - Characters are sent to `DbgFp'. */ void DspFlnm(u_int size, char *flnm) { int i; (void) fprintf(DbgFp, "\n\t\tFile Name (%u): <", size); for (i = 0; i < size; i++) (void) fputc(*flnm++, DbgFp); (void) fputs(">\n", DbgFp); } /* ** NewClient -- allocate memory for a new CLIENT. ** ** Parameters: ** addr - RMP (Ethernet) address of new client. ** ** Returns: ** Ptr to new CLIENT or NULL if we ran out of memory. ** ** Side Effects: ** - Memory will be malloc'd for the new CLIENT. ** - If malloc() fails, a log message will be generated. */ CLIENT * NewClient(u_int8_t *addr) { CLIENT *ctmp; if ((ctmp = (CLIENT *) malloc(sizeof(CLIENT))) == NULL) { syslog(LOG_ERR, "NewClient: out of memory (%s)", GetEtherAddr(addr)); return(NULL); } memset(ctmp, 0, sizeof(CLIENT)); memmove(&ctmp->addr[0], addr, RMP_ADDRLEN); return(ctmp); } /* ** FreeClient -- free linked list of Clients. ** ** Parameters: ** None. ** ** Returns: ** Nothing. ** ** Side Effects: ** - All malloc'd memory associated with the linked list of ** CLIENTS will be free'd; `Clients' will be set to NULL. ** ** Warnings: ** - This routine must be called with SIGHUP blocked. */ void FreeClients(void) { CLIENT *ctmp; while (Clients != NULL) { ctmp = Clients; Clients = Clients->next; FreeClient(ctmp); } } /* ** NewStr -- allocate memory for a character array. ** ** Parameters: ** str - null terminated character array. ** ** Returns: ** Ptr to new character array or NULL if we ran out of memory. ** ** Side Effects: ** - Memory will be malloc'd for the new character array. ** - If malloc() fails, a log message will be generated. */ char * NewStr(char *str) { char *stmp; if ((stmp = (char *)malloc((unsigned) (strlen(str)+1))) == NULL) { syslog(LOG_ERR, "NewStr: out of memory (%s)", str); return(NULL); } (void) strcpy(stmp, str); return(stmp); } /* ** To save time, NewConn and FreeConn maintain a cache of one RMPCONN ** in `LastFree' (defined below). */ static RMPCONN *LastFree = NULL; /* ** NewConn -- allocate memory for a new RMPCONN connection. ** ** Parameters: ** rconn - initialization template for new connection. ** ** Returns: ** Ptr to new RMPCONN or NULL if we ran out of memory. ** ** Side Effects: ** - Memory may be malloc'd for the new RMPCONN (if not cached). ** - If malloc() fails, a log message will be generated. */ RMPCONN * NewConn(RMPCONN *rconn) { RMPCONN *rtmp; if (LastFree == NULL) { /* nothing cached; make a new one */ if ((rtmp = (RMPCONN *) malloc(sizeof(RMPCONN))) == NULL) { syslog(LOG_ERR, "NewConn: out of memory (%s)", EnetStr(rconn)); return(NULL); } } else { /* use the cached RMPCONN */ rtmp = LastFree; LastFree = NULL; } /* * Copy template into `rtmp', init file descriptor to `-1' and * set ptr to next elem NULL. */ memmove((char *)rtmp, (char *)rconn, sizeof(RMPCONN)); rtmp->bootfd = -1; rtmp->next = NULL; return(rtmp); } /* ** FreeConn -- Free memory associated with an RMPCONN connection. ** ** Parameters: ** rtmp - ptr to RMPCONN to be free'd. ** ** Returns: ** Nothing. ** ** Side Effects: ** - Memory associated with `rtmp' may be free'd (or cached). ** - File desc associated with `rtmp->bootfd' will be closed. */ void FreeConn(RMPCONN *rtmp) { /* * If the file descriptor is in use, close the file. */ if (rtmp->bootfd >= 0) { (void) close(rtmp->bootfd); rtmp->bootfd = -1; } if (LastFree == NULL) /* cache for next time */ rtmp = LastFree; else /* already one cached; free this one */ free((char *)rtmp); } /* ** FreeConns -- free linked list of RMPCONN connections. ** ** Parameters: ** None. ** ** Returns: ** Nothing. ** ** Side Effects: ** - All malloc'd memory associated with the linked list of ** connections will be free'd; `RmpConns' will be set to NULL. ** - If LastFree is != NULL, it too will be free'd & NULL'd. ** ** Warnings: ** - This routine must be called with SIGHUP blocked. */ void FreeConns(void) { RMPCONN *rtmp; while (RmpConns != NULL) { rtmp = RmpConns; RmpConns = RmpConns->next; FreeConn(rtmp); } if (LastFree != NULL) { free((char *)LastFree); LastFree = NULL; } } /* ** AddConn -- Add a connection to the linked list of connections. ** ** Parameters: ** rconn - connection to be added. ** ** Returns: ** Nothing. ** ** Side Effects: ** - RmpConn will point to new connection. ** ** Warnings: ** - This routine must be called with SIGHUP blocked. */ void AddConn(RMPCONN *rconn) { if (RmpConns != NULL) rconn->next = RmpConns; RmpConns = rconn; } /* ** FindConn -- Find a connection in the linked list of connections. ** ** We use the RMP (Ethernet) address as the basis for determining ** if this is the same connection. According to the Remote Maint ** Protocol, we can only have one connection with any machine. ** ** Parameters: ** rconn - connection to be found. ** ** Returns: ** Matching connection from linked list or NULL if not found. ** ** Side Effects: ** None. ** ** Warnings: ** - This routine must be called with SIGHUP blocked. */ RMPCONN * FindConn(RMPCONN *rconn) { RMPCONN *rtmp; for (rtmp = RmpConns; rtmp != NULL; rtmp = rtmp->next) if (bcmp((char *)&rconn->rmp.hp_hdr.saddr[0], (char *)&rtmp->rmp.hp_hdr.saddr[0], RMP_ADDRLEN) == 0) break; return(rtmp); } /* ** RemoveConn -- Remove a connection from the linked list of connections. ** ** Parameters: ** rconn - connection to be removed. ** ** Returns: ** Nothing. ** ** Side Effects: ** - If found, an RMPCONN will cease to exist and it will ** be removed from the linked list. ** ** Warnings: ** - This routine must be called with SIGHUP blocked. */ void RemoveConn(RMPCONN *rconn) { RMPCONN *thisrconn, *lastrconn; if (RmpConns == rconn) { /* easy case */ RmpConns = RmpConns->next; FreeConn(rconn); } else { /* must traverse linked list */ lastrconn = RmpConns; /* set back ptr */ thisrconn = lastrconn->next; /* set current ptr */ while (thisrconn != NULL) { if (rconn == thisrconn) { /* found it */ lastrconn->next = thisrconn->next; FreeConn(thisrconn); break; } lastrconn = thisrconn; thisrconn = thisrconn->next; } } } diff --git a/libexec/revnetgroup/hash.c b/libexec/revnetgroup/hash.c index 2dd1a3f91d74..db8e95e3040c 100644 --- a/libexec/revnetgroup/hash.c +++ b/libexec/revnetgroup/hash.c @@ -1,210 +1,205 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1995 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include "hash.h" /* * This hash function is stolen directly from the * Berkeley DB package. It already exists inside libc, but * it's declared static which prevents us from calling it * from here. */ /* * OZ's original sdbm hash */ u_int32_t hash(const void *keyarg, size_t len) { const u_char *key; size_t loop; u_int32_t h; #define HASHC h = *key++ + 65599 * h h = 0; key = keyarg; if (len > 0) { loop = (len + 8 - 1) >> 3; switch (len & (8 - 1)) { case 0: do { HASHC; /* FALLTHROUGH */ case 7: HASHC; /* FALLTHROUGH */ case 6: HASHC; /* FALLTHROUGH */ case 5: HASHC; /* FALLTHROUGH */ case 4: HASHC; /* FALLTHROUGH */ case 3: HASHC; /* FALLTHROUGH */ case 2: HASHC; /* FALLTHROUGH */ case 1: HASHC; } while (--loop); } } return (h); } /* * Generate a hash value for a given key (character string). * We mask off all but the lower 8 bits since our table array * can only hold 256 elements. */ u_int32_t hashkey(char *key) { if (key == NULL) return (-1); return(hash((void *)key, strlen(key)) & HASH_MASK); } /* Find an entry in the hash table (may be hanging off a linked list). */ char * lookup(struct group_entry *table[], char *key) { struct group_entry *cur; cur = table[hashkey(key)]; while (cur) { if (!strcmp(cur->key, key)) return(cur->data); cur = cur->next; } return(NULL); } /* * Store an entry in the main netgroup hash table. Here's how this * works: the table can only be so big when we initialize it (TABLESIZE) * but the number of netgroups in the /etc/netgroup file could easily be * much larger than the table. Since our hash values are adjusted to * never be greater than TABLESIZE too, this means it won't be long before * we find ourselves with two keys that hash to the same value. * * One way to deal with this is to malloc(2) a second table and start * doing indirection, but this is a pain in the butt and it's not worth * going to all that trouble for a dinky little program like this. Instead, * we turn each table entry into a linked list and simply link keys * with the same hash value together at the same index location within * the table. * * That's a lot of comment for such a small piece of code, isn't it. */ void store(struct group_entry *table[], char *key, char *data) { struct group_entry *new; u_int32_t i; i = hashkey(key); new = (struct group_entry *)malloc(sizeof(struct group_entry)); new->key = strdup(key); new->data = strdup(data); new->next = table[i]; table[i] = new; return; } /* * Store a group member entry and/or update its grouplist. This is * a bit more complicated than the previous function since we have to * maintain not only the hash table of group members, each group member * structure also has a linked list of groups hung off it. If handed * a member name that we haven't encountered before, we have to do * two things: add that member to the table (possibly hanging them * off the end of a linked list, as above), and add a group name to * the member's grouplist list. If we're handed a name that already has * an entry in the table, then we just have to do one thing, which is * to update its grouplist. */ void mstore(struct member_entry *table[], char *key, char *data, char *domain) { struct member_entry *cur, *new; struct grouplist *tmp; u_int32_t i; i = hashkey(key); cur = table[i]; tmp = (struct grouplist *)malloc(sizeof(struct grouplist)); tmp->groupname = strdup(data); tmp->next = NULL; /* Check if all we have to do is insert a new groupname. */ while (cur) { if (!strcmp(cur->key, key)) { tmp->next = cur->groups; cur->groups = tmp; return; } cur = cur->next; } /* Didn't find a match -- add the whole mess to the table. */ new = (struct member_entry *)malloc(sizeof(struct member_entry)); new->key = strdup(key); new->domain = domain ? strdup(domain) : "*"; new->groups = tmp; new->next = table[i]; table[i] = new; return; } diff --git a/libexec/revnetgroup/parse_netgroup.c b/libexec/revnetgroup/parse_netgroup.c index a1e954164ea9..3d6a7939fa1d 100644 --- a/libexec/revnetgroup/parse_netgroup.c +++ b/libexec/revnetgroup/parse_netgroup.c @@ -1,362 +1,357 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Rick Macklem at The University of Guelph. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - /* * This is a specially hacked-up version of getnetgrent.c used to parse * data from the stored hash table of netgroup info rather than from a * file. It's used mainly for the parse_netgroup() function. All the YP * stuff and file support has been stripped out since it isn't needed. */ #include #include #include #include #include #include "hash.h" /* * Static Variables and functions used by setnetgrent(), getnetgrent() and * __endnetgrent(). * There are two linked lists: * - linelist is just used by setnetgrent() to parse the net group file via. * parse_netgrp() * - netgrp is the list of entries for the current netgroup */ struct linelist { struct linelist *l_next; /* Chain ptr. */ int l_parsed; /* Flag for cycles */ char *l_groupname; /* Name of netgroup */ char *l_line; /* Netgroup entrie(s) to be parsed */ }; struct netgrp { struct netgrp *ng_next; /* Chain ptr */ char *ng_str[3]; /* Field pointers, see below */ }; #define NG_HOST 0 /* Host name */ #define NG_USER 1 /* User name */ #define NG_DOM 2 /* and Domain name */ static struct linelist *linehead = (struct linelist *)0; static struct netgrp *nextgrp = (struct netgrp *)0; static struct { struct netgrp *gr; char *grname; } grouphead = { (struct netgrp *)0, (char *)0, }; static int parse_netgrp(char *group); static struct linelist *read_for_group(char *group); extern struct group_entry *gtable[]; /* * setnetgrent() * Parse the netgroup file looking for the netgroup and build the list * of netgrp structures. Let parse_netgrp() and read_for_group() do * most of the work. */ void __setnetgrent(char *group) { /* Sanity check */ if (group == NULL || !strlen(group)) return; if (grouphead.gr == (struct netgrp *)0 || strcmp(group, grouphead.grname)) { __endnetgrent(); if (parse_netgrp(group)) __endnetgrent(); else { grouphead.grname = (char *) malloc(strlen(group) + 1); strcpy(grouphead.grname, group); } } nextgrp = grouphead.gr; } /* * Get the next netgroup off the list. */ int __getnetgrent(char **hostp, char **userp, char **domp) { if (nextgrp) { *hostp = nextgrp->ng_str[NG_HOST]; *userp = nextgrp->ng_str[NG_USER]; *domp = nextgrp->ng_str[NG_DOM]; nextgrp = nextgrp->ng_next; return (1); } return (0); } /* * __endnetgrent() - cleanup */ void __endnetgrent(void) { struct linelist *lp, *olp; struct netgrp *gp, *ogp; lp = linehead; while (lp) { olp = lp; lp = lp->l_next; free(olp->l_groupname); free(olp->l_line); free((char *)olp); } linehead = (struct linelist *)0; if (grouphead.grname) { free(grouphead.grname); grouphead.grname = (char *)0; } gp = grouphead.gr; while (gp) { ogp = gp; gp = gp->ng_next; if (ogp->ng_str[NG_HOST]) free(ogp->ng_str[NG_HOST]); if (ogp->ng_str[NG_USER]) free(ogp->ng_str[NG_USER]); if (ogp->ng_str[NG_DOM]) free(ogp->ng_str[NG_DOM]); free((char *)ogp); } grouphead.gr = (struct netgrp *)0; } /* * Parse the netgroup file setting up the linked lists. */ static int parse_netgrp(char *group) { char *spos, *epos; int len, strpos; #ifdef DEBUG int fields; #endif char *pos, *gpos; struct netgrp *grp; struct linelist *lp = linehead; /* * First, see if the line has already been read in. */ while (lp) { if (!strcmp(group, lp->l_groupname)) break; lp = lp->l_next; } if (lp == (struct linelist *)0 && (lp = read_for_group(group)) == (struct linelist *)0) return (1); if (lp->l_parsed) { #ifdef DEBUG /* * This error message is largely superfluous since the * code handles the error condition successfully, and * spewing it out from inside libc can actually hose * certain programs. */ warnx("cycle in netgroup %s", lp->l_groupname); #endif return (1); } else lp->l_parsed = 1; pos = lp->l_line; /* Watch for null pointer dereferences, dammit! */ while (pos != NULL && *pos != '\0') { if (*pos == '(') { grp = (struct netgrp *)malloc(sizeof (struct netgrp)); bzero((char *)grp, sizeof (struct netgrp)); grp->ng_next = grouphead.gr; grouphead.gr = grp; pos++; gpos = strsep(&pos, ")"); #ifdef DEBUG fields = 0; #endif for (strpos = 0; strpos < 3; strpos++) { if ((spos = strsep(&gpos, ","))) { #ifdef DEBUG fields++; #endif while (*spos == ' ' || *spos == '\t') spos++; if ((epos = strpbrk(spos, " \t"))) { *epos = '\0'; len = epos - spos; } else len = strlen(spos); if (len > 0) { grp->ng_str[strpos] = (char *) malloc(len + 1); bcopy(spos, grp->ng_str[strpos], len + 1); } } else { /* * All other systems I've tested * return NULL for empty netgroup * fields. It's up to user programs * to handle the NULLs appropriately. */ grp->ng_str[strpos] = NULL; } } #ifdef DEBUG /* * Note: on other platforms, malformed netgroup * entries are not normally flagged. While we * can catch bad entries and report them, we should * stay silent by default for compatibility's sake. */ if (fields < 3) warnx("bad entry (%s%s%s%s%s) in netgroup \"%s\"", grp->ng_str[NG_HOST] == NULL ? "" : grp->ng_str[NG_HOST], grp->ng_str[NG_USER] == NULL ? "" : ",", grp->ng_str[NG_USER] == NULL ? "" : grp->ng_str[NG_USER], grp->ng_str[NG_DOM] == NULL ? "" : ",", grp->ng_str[NG_DOM] == NULL ? "" : grp->ng_str[NG_DOM], lp->l_groupname); #endif } else { spos = strsep(&pos, ", \t"); if (parse_netgrp(spos)) continue; } /* Watch for null pointer dereferences, dammit! */ if (pos != NULL) while (*pos == ' ' || *pos == ',' || *pos == '\t') pos++; } return (0); } /* * Read the netgroup file and save lines until the line for the netgroup * is found. Return 1 if eof is encountered. */ static struct linelist * read_for_group(char *group) { char *pos, *spos, *linep = NULL, *olinep = NULL; int len, olen; int cont; struct linelist *lp; char line[LINSIZ + 1]; char *data = NULL; data = lookup (gtable, group); sprintf(line, "%s %s", group, data); pos = (char *)&line; #ifdef CANT_HAPPEN if (*pos == '#') continue; #endif while (*pos == ' ' || *pos == '\t') pos++; spos = pos; while (*pos != ' ' && *pos != '\t' && *pos != '\n' && *pos != '\0') pos++; len = pos - spos; while (*pos == ' ' || *pos == '\t') pos++; if (*pos != '\n' && *pos != '\0') { lp = (struct linelist *)malloc(sizeof (*lp)); lp->l_parsed = 0; lp->l_groupname = (char *)malloc(len + 1); bcopy(spos, lp->l_groupname, len); *(lp->l_groupname + len) = '\0'; len = strlen(pos); olen = 0; /* * Loop around handling line continuations. */ do { if (*(pos + len - 1) == '\n') len--; if (*(pos + len - 1) == '\\') { len--; cont = 1; } else cont = 0; if (len > 0) { linep = (char *)malloc(olen + len + 1); if (olen > 0) { bcopy(olinep, linep, olen); free(olinep); } bcopy(pos, linep + olen, len); olen += len; *(linep + olen) = '\0'; olinep = linep; } #ifdef CANT_HAPPEN if (cont) { if (fgets(line, LINSIZ, netf)) { pos = line; len = strlen(pos); } else cont = 0; } #endif } while (cont); lp->l_line = linep; lp->l_next = linehead; linehead = lp; #ifdef CANT_HAPPEN /* * If this is the one we wanted, we are done. */ if (!strcmp(lp->l_groupname, group)) #endif return (lp); } return ((struct linelist *)0); } diff --git a/libexec/revnetgroup/revnetgroup.c b/libexec/revnetgroup/revnetgroup.c index 11fba515a7ba..34ec0d9491c4 100644 --- a/libexec/revnetgroup/revnetgroup.c +++ b/libexec/revnetgroup/revnetgroup.c @@ -1,181 +1,176 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1995 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * reverse netgroup map generator program * * Written by Bill Paul * Center for Telecommunications Research * Columbia University, New York City */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include "hash.h" /* Default location of netgroup file. */ char *netgroup = "/etc/netgroup"; /* Stored hash table version of 'forward' netgroup database. */ struct group_entry *gtable[TABLESIZE]; /* * Stored hash table of 'reverse' netgroup member database * which we will construct. */ struct member_entry *mtable[TABLESIZE]; static void usage(void) { fprintf (stderr,"usage: revnetgroup -u | -h [-f netgroup_file]\n"); exit(1); } int main(int argc, char *argv[]) { FILE *fp; char readbuf[LINSIZ]; struct group_entry *gcur; struct member_entry *mcur; char *host, *user, *domain; int ch; char *key = NULL, *data = NULL; int hosts = -1, i; if (argc < 2) usage(); while ((ch = getopt(argc, argv, "uhf:")) != -1) { switch(ch) { case 'u': if (hosts != -1) { warnx("please use only one of -u or -h"); usage(); } hosts = 0; break; case 'h': if (hosts != -1) { warnx("please use only one of -u or -h"); usage(); } hosts = 1; break; case 'f': netgroup = optarg; break; default: usage(); break; } } if (hosts == -1) usage(); if (strcmp(netgroup, "-")) { if ((fp = fopen(netgroup, "r")) == NULL) { err(1, "%s", netgroup); } } else { fp = stdin; } /* Stuff all the netgroup names and members into a hash table. */ while (fgets(readbuf, LINSIZ, fp)) { if (readbuf[0] == '#') continue; /* handle backslash line continuations */ while(readbuf[strlen(readbuf) - 2] == '\\') { fgets((char *)&readbuf[strlen(readbuf) - 2], sizeof(readbuf) - strlen(readbuf), fp); } data = NULL; if ((data = (char *)(strpbrk(readbuf, " \t") + 1)) < (char *)2) continue; key = (char *)&readbuf; *(data - 1) = '\0'; store(gtable, key, data); } fclose(fp); /* * Find all members of each netgroup and keep track of which * group they belong to. */ for (i = 0; i < TABLESIZE; i++) { gcur = gtable[i]; while(gcur) { __setnetgrent(gcur->key); while(__getnetgrent(&host, &user, &domain) != 0) { if (hosts ? host && strcmp(host,"-") : user && strcmp(user, "-")) mstore(mtable, hosts ? host : user, gcur->key, domain); } gcur = gcur->next; } } /* Release resources used by the netgroup parser code. */ __endnetgrent(); /* Spew out the results. */ for (i = 0; i < TABLESIZE; i++) { mcur = mtable[i]; while(mcur) { struct grouplist *tmp; printf ("%s.%s\t", mcur->key, mcur->domain); tmp = mcur->groups; while(tmp) { printf ("%s", tmp->groupname); tmp = tmp->next; if (tmp) printf(","); } mcur = mcur->next; printf ("\n"); } } /* Let the OS free all our resources. */ exit(0); } diff --git a/libexec/rpc.rstatd/rstat_proc.c b/libexec/rpc.rstatd/rstat_proc.c index 1f1bb7cf4048..cc488657c077 100644 --- a/libexec/rpc.rstatd/rstat_proc.c +++ b/libexec/rpc.rstatd/rstat_proc.c @@ -1,478 +1,476 @@ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape * media and as a part of the software program in whole or part. Users * may copy or modify Sun RPC without charge, but are not authorized * to license or distribute it to anyone else except as part of a product or * program developed by the user. * * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. * * Sun RPC is provided with no support and without any obligation on the * part of Sun Microsystems, Inc. to assist in its use, correction, * modification or enhancement. * * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC * OR ANY PART THEREOF. * * In no event will Sun Microsystems, Inc. be liable for any lost revenue * or profits or other special, indirect and consequential damages, even if * Sun has been advised of the possibility of such damages. * * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 */ #ifndef lint #if 0 static char sccsid[] = "from: @(#)rpc.rstatd.c 1.1 86/09/25 Copyr 1984 Sun Micro"; static char sccsid[] = "from: @(#)rstat_proc.c 2.2 88/08/01 4.0 RPCSRC"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* * rstat service: built with rstat.x and derived from rpc.rstatd.c * * Copyright (c) 1984 by Sun Microsystems, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef FSHIFT /* Use protocol's shift and scale values */ #undef FSCALE #undef if_ipackets #undef if_ierrors #undef if_opackets #undef if_oerrors #undef if_collisions #include int haveadisk(void); void updatexfers(int, int *); int stats_service(void); extern int from_inetd; int sincelastreq = 0; /* number of alarms since last request */ extern int closedown; union { struct stats s1; struct statsswtch s2; struct statstime s3; } stats_all; void updatestat(); static int stat_is_init = 0; static int cp_time_xlat[RSTAT_CPUSTATES] = { CP_USER, CP_NICE, CP_SYS, CP_IDLE }; static long bsd_cp_time[CPUSTATES]; #ifndef FSCALE #define FSCALE (1 << 8) #endif void stat_init(void) { stat_is_init = 1; alarm(0); updatestat(); (void) signal(SIGALRM, updatestat); alarm(1); } statstime * rstatproc_stats_3_svc(void *argp, struct svc_req *rqstp) { if (! stat_is_init) stat_init(); sincelastreq = 0; return(&stats_all.s3); } statsswtch * rstatproc_stats_2_svc(void *argp, struct svc_req *rqstp) { if (! stat_is_init) stat_init(); sincelastreq = 0; return(&stats_all.s2); } stats * rstatproc_stats_1_svc(void *argp, struct svc_req *rqstp) { if (! stat_is_init) stat_init(); sincelastreq = 0; return(&stats_all.s1); } u_int * rstatproc_havedisk_3_svc(void *argp, struct svc_req *rqstp) { static u_int have; if (! stat_is_init) stat_init(); sincelastreq = 0; have = haveadisk(); return(&have); } u_int * rstatproc_havedisk_2_svc(void *argp, struct svc_req *rqstp) { return(rstatproc_havedisk_3_svc(argp, rqstp)); } u_int * rstatproc_havedisk_1_svc(void *argp, struct svc_req *rqstp) { return(rstatproc_havedisk_3_svc(argp, rqstp)); } void updatestat(void) { int i, hz; struct clockinfo clockrate; struct ifmibdata ifmd; double avrun[3]; struct timeval tm, btm; int mib[6]; size_t len; uint64_t val; int ifcount; #ifdef DEBUG fprintf(stderr, "entering updatestat\n"); #endif if (sincelastreq >= closedown) { #ifdef DEBUG fprintf(stderr, "about to closedown\n"); #endif if (from_inetd) exit(0); else { stat_is_init = 0; return; } } sincelastreq++; mib[0] = CTL_KERN; mib[1] = KERN_CLOCKRATE; len = sizeof clockrate; if (sysctl(mib, 2, &clockrate, &len, 0, 0) < 0) { syslog(LOG_ERR, "sysctl(kern.clockrate): %m"); exit(1); } hz = clockrate.hz; len = sizeof(bsd_cp_time); if (sysctlbyname("kern.cp_time", bsd_cp_time, &len, 0, 0) < 0) { syslog(LOG_ERR, "sysctl(kern.cp_time): %m"); exit(1); } for(i = 0; i < RSTAT_CPUSTATES ; i++) stats_all.s1.cp_time[i] = bsd_cp_time[cp_time_xlat[i]]; (void)getloadavg(avrun, sizeof(avrun) / sizeof(avrun[0])); stats_all.s2.avenrun[0] = avrun[0] * FSCALE; stats_all.s2.avenrun[1] = avrun[1] * FSCALE; stats_all.s2.avenrun[2] = avrun[2] * FSCALE; mib[0] = CTL_KERN; mib[1] = KERN_BOOTTIME; len = sizeof btm; if (sysctl(mib, 2, &btm, &len, 0, 0) < 0) { syslog(LOG_ERR, "sysctl(kern.boottime): %m"); exit(1); } stats_all.s2.boottime.tv_sec = btm.tv_sec; stats_all.s2.boottime.tv_usec = btm.tv_usec; #ifdef DEBUG fprintf(stderr, "%d %d %d %d\n", stats_all.s1.cp_time[0], stats_all.s1.cp_time[1], stats_all.s1.cp_time[2], stats_all.s1.cp_time[3]); #endif #define FETCH_CNT(stat, cnt) do { \ len = sizeof(uint64_t); \ if (sysctlbyname("vm.stats." #cnt , &val, &len, NULL, 0) < 0) { \ syslog(LOG_ERR, "sysctl(vm.stats." #cnt "): %m"); \ exit(1); \ } \ stat = val; \ } while (0) FETCH_CNT(stats_all.s1.v_pgpgin, vm.v_vnodepgsin); FETCH_CNT(stats_all.s1.v_pgpgout, vm.v_vnodepgsout); FETCH_CNT(stats_all.s1.v_pswpin, vm.v_swappgsin); FETCH_CNT(stats_all.s1.v_pswpout, vm.v_swappgsout); FETCH_CNT(stats_all.s1.v_intr, sys.v_intr); FETCH_CNT(stats_all.s2.v_swtch, sys.v_swtch); (void)gettimeofday(&tm, NULL); stats_all.s1.v_intr -= hz*(tm.tv_sec - btm.tv_sec) + hz*(tm.tv_usec - btm.tv_usec)/1000000; /* update disk transfers */ updatexfers(RSTAT_DK_NDRIVE, stats_all.s1.dk_xfer); mib[0] = CTL_NET; mib[1] = PF_LINK; mib[2] = NETLINK_GENERIC; mib[3] = IFMIB_SYSTEM; mib[4] = IFMIB_IFCOUNT; len = sizeof ifcount; if (sysctl(mib, 5, &ifcount, &len, 0, 0) < 0) { syslog(LOG_ERR, "sysctl(net.link.generic.system.ifcount): %m"); exit(1); } stats_all.s1.if_ipackets = 0; stats_all.s1.if_opackets = 0; stats_all.s1.if_ierrors = 0; stats_all.s1.if_oerrors = 0; stats_all.s1.if_collisions = 0; for (i = 1; i <= ifcount; i++) { len = sizeof ifmd; mib[3] = IFMIB_IFDATA; mib[4] = i; mib[5] = IFDATA_GENERAL; if (sysctl(mib, 6, &ifmd, &len, 0, 0) < 0) { if (errno == ENOENT) continue; syslog(LOG_ERR, "sysctl(net.link.ifdata.%d.general)" ": %m", i); exit(1); } stats_all.s1.if_ipackets += ifmd.ifmd_data.ifi_ipackets; stats_all.s1.if_opackets += ifmd.ifmd_data.ifi_opackets; stats_all.s1.if_ierrors += ifmd.ifmd_data.ifi_ierrors; stats_all.s1.if_oerrors += ifmd.ifmd_data.ifi_oerrors; stats_all.s1.if_collisions += ifmd.ifmd_data.ifi_collisions; } (void)gettimeofday(&tm, NULL); stats_all.s3.curtime.tv_sec = tm.tv_sec; stats_all.s3.curtime.tv_usec = tm.tv_usec; alarm(1); } /* * returns true if have a disk */ int haveadisk(void) { register int i; struct statinfo stats; int num_devices, retval = 0; if ((num_devices = devstat_getnumdevs(NULL)) < 0) { syslog(LOG_ERR, "rstatd: can't get number of devices: %s", devstat_errbuf); exit(1); } if (devstat_checkversion(NULL) < 0) { syslog(LOG_ERR, "rstatd: %s", devstat_errbuf); exit(1); } stats.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo)); bzero(stats.dinfo, sizeof(struct devinfo)); if (devstat_getdevs(NULL, &stats) == -1) { syslog(LOG_ERR, "rstatd: can't get device list: %s", devstat_errbuf); exit(1); } for (i = 0; i < stats.dinfo->numdevs; i++) { if (((stats.dinfo->devices[i].device_type & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_DIRECT) && ((stats.dinfo->devices[i].device_type & DEVSTAT_TYPE_PASS) == 0)) { retval = 1; break; } } if (stats.dinfo->mem_ptr) free(stats.dinfo->mem_ptr); free(stats.dinfo); return(retval); } void updatexfers(int numdevs, int *devs) { register int i, j, k, t; struct statinfo stats; int num_devices = 0; u_int64_t total_transfers; if ((num_devices = devstat_getnumdevs(NULL)) < 0) { syslog(LOG_ERR, "rstatd: can't get number of devices: %s", devstat_errbuf); exit(1); } if (devstat_checkversion(NULL) < 0) { syslog(LOG_ERR, "rstatd: %s", devstat_errbuf); exit(1); } stats.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo)); bzero(stats.dinfo, sizeof(struct devinfo)); if (devstat_getdevs(NULL, &stats) == -1) { syslog(LOG_ERR, "rstatd: can't get device list: %s", devstat_errbuf); exit(1); } for (i = 0, j = 0; i < stats.dinfo->numdevs && j < numdevs; i++) { if (((stats.dinfo->devices[i].device_type & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_DIRECT) && ((stats.dinfo->devices[i].device_type & DEVSTAT_TYPE_PASS) == 0)) { total_transfers = 0; for (k = 0; k < DEVSTAT_N_TRANS_FLAGS; k++) total_transfers += stats.dinfo->devices[i].operations[k]; /* * XXX KDM If the total transfers for this device * are greater than the amount we can fit in a * signed integer, just set them to the maximum * amount we can fit in a signed integer. I have a * feeling that the rstat protocol assumes 32-bit * integers, so this could well break on a 64-bit * architecture like the Alpha. */ if (total_transfers > INT_MAX) t = INT_MAX; else t = total_transfers; devs[j] = t; j++; } } if (stats.dinfo->mem_ptr) free(stats.dinfo->mem_ptr); free(stats.dinfo); } void rstat_service(struct svc_req *rqstp, SVCXPRT *transp) { union { int fill; } argument; void *result; xdrproc_t xdr_argument, xdr_result; typedef void *(svc_cb)(void *arg, struct svc_req *rqstp); svc_cb *local; switch (rqstp->rq_proc) { case NULLPROC: (void)svc_sendreply(transp, (xdrproc_t)xdr_void, NULL); goto leave; case RSTATPROC_STATS: xdr_argument = (xdrproc_t)xdr_void; xdr_result = (xdrproc_t)xdr_statstime; switch (rqstp->rq_vers) { case RSTATVERS_ORIG: local = (svc_cb *)rstatproc_stats_1_svc; break; case RSTATVERS_SWTCH: local = (svc_cb *)rstatproc_stats_2_svc; break; case RSTATVERS_TIME: local = (svc_cb *)rstatproc_stats_3_svc; break; default: svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME); goto leave; /*NOTREACHED*/ } break; case RSTATPROC_HAVEDISK: xdr_argument = (xdrproc_t)xdr_void; xdr_result = (xdrproc_t)xdr_u_int; switch (rqstp->rq_vers) { case RSTATVERS_ORIG: local = (svc_cb *)rstatproc_havedisk_1_svc; break; case RSTATVERS_SWTCH: local = (svc_cb *)rstatproc_havedisk_2_svc; break; case RSTATVERS_TIME: local = (svc_cb *)rstatproc_havedisk_3_svc; break; default: svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME); goto leave; /*NOTREACHED*/ } break; default: svcerr_noproc(transp); goto leave; } bzero((char *)&argument, sizeof(argument)); if (!svc_getargs(transp, xdr_argument, &argument)) { svcerr_decode(transp); goto leave; } result = (*local)(&argument, rqstp); if (result != NULL && !svc_sendreply(transp, xdr_result, result)) { svcerr_systemerr(transp); } if (!svc_freeargs(transp, xdr_argument, &argument)) errx(1, "unable to free arguments"); leave: if (from_inetd) exit(0); } diff --git a/libexec/rpc.rstatd/rstatd.c b/libexec/rpc.rstatd/rstatd.c index 6a6b09b9ae0c..7cc3bac71c5d 100644 --- a/libexec/rpc.rstatd/rstatd.c +++ b/libexec/rpc.rstatd/rstatd.c @@ -1,131 +1,126 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1993, John Brezak * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include extern void rstat_service(struct svc_req *, SVCXPRT *); int from_inetd = 1; /* started from inetd ? */ int closedown = 20; /* how long to wait before going dormant */ void cleanup(int sig __unused) { (void) rpcb_unset(RSTATPROG, RSTATVERS_TIME, NULL); (void) rpcb_unset(RSTATPROG, RSTATVERS_SWTCH, NULL); (void) rpcb_unset(RSTATPROG, RSTATVERS_ORIG, NULL); exit(0); } int main(int argc, char *argv[]) { SVCXPRT *transp; int ok; struct sockaddr_storage from; socklen_t fromlen; if (argc == 2) closedown = atoi(argv[1]); if (closedown <= 0) closedown = 20; /* * See if inetd started us */ fromlen = sizeof(from); if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) { from_inetd = 0; } if (!from_inetd) { daemon(0, 0); (void)rpcb_unset(RSTATPROG, RSTATVERS_TIME, NULL); (void)rpcb_unset(RSTATPROG, RSTATVERS_SWTCH, NULL); (void)rpcb_unset(RSTATPROG, RSTATVERS_ORIG, NULL); (void) signal(SIGINT, cleanup); (void) signal(SIGTERM, cleanup); (void) signal(SIGHUP, cleanup); } openlog("rpc.rstatd", LOG_CONS|LOG_PID, LOG_DAEMON); if (from_inetd) { transp = svc_tli_create(0, NULL, NULL, 0, 0); if (transp == NULL) { syslog(LOG_ERR, "cannot create udp service."); exit(1); } ok = svc_reg(transp, RSTATPROG, RSTATVERS_TIME, rstat_service, NULL); } else ok = svc_create(rstat_service, RSTATPROG, RSTATVERS_TIME, "udp"); if (!ok) { syslog(LOG_ERR, "unable to register (RSTATPROG, RSTATVERS_TIME, %s)", (!from_inetd)?"udp":"(inetd)"); exit(1); } if (from_inetd) ok = svc_reg(transp, RSTATPROG, RSTATVERS_SWTCH, rstat_service, NULL); else ok = svc_create(rstat_service, RSTATPROG, RSTATVERS_SWTCH, "udp"); if (!ok) { syslog(LOG_ERR, "unable to register (RSTATPROG, RSTATVERS_SWTCH, %s)", (!from_inetd)?"udp":"(inetd)"); exit(1); } if (from_inetd) ok = svc_reg(transp, RSTATPROG, RSTATVERS_ORIG, rstat_service, NULL); else ok = svc_create(rstat_service, RSTATPROG, RSTATVERS_ORIG, "udp"); if (!ok) { syslog(LOG_ERR, "unable to register (RSTATPROG, RSTATVERS_ORIG, %s)", (!from_inetd)?"udp":"(inetd)"); exit(1); } svc_run(); syslog(LOG_ERR, "svc_run returned"); exit(1); } diff --git a/libexec/rpc.rusersd/rusers_proc.c b/libexec/rpc.rusersd/rusers_proc.c index bfb65dadb004..3bc4169a989f 100644 --- a/libexec/rpc.rusersd/rusers_proc.c +++ b/libexec/rpc.rusersd/rusers_proc.c @@ -1,334 +1,329 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1993, John Brezak * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #ifdef DEBUG #include #endif #include #include #include #include #include #include #include #ifdef XIDLE #include #include #include #endif #include #include "extern.h" #ifndef _PATH_DEV #define _PATH_DEV "/dev" #endif static utmpidle utmp_idle[MAXUSERS]; static utmp old_utmp[MAXUSERS]; static struct utmpx utmp_list[MAXUSERS]; #ifdef XIDLE static Display *dpy; static jmp_buf openAbort; static void abortOpen(void) { longjmp (openAbort, 1); } XqueryIdle(char *display) { int first_event, first_error; Time IdleTime; (void) signal (SIGALRM, abortOpen); (void) alarm ((unsigned) 10); if (!setjmp (openAbort)) { if (!(dpy= XOpenDisplay(display))) { syslog(LOG_ERR, "Cannot open display %s", display); return(-1); } if (XidleQueryExtension(dpy, &first_event, &first_error)) { if (!XGetIdleTime(dpy, &IdleTime)) { syslog(LOG_ERR, "%s: unable to get idle time", display); return(-1); } } else { syslog(LOG_ERR, "%s: Xidle extension not loaded", display); return(-1); } XCloseDisplay(dpy); } else { syslog(LOG_ERR, "%s: server grabbed for over 10 seconds", display); return(-1); } (void) signal (SIGALRM, SIG_DFL); (void) alarm ((unsigned) 0); IdleTime /= 1000; return((IdleTime + 30) / 60); } #endif static u_int getidle(const char *tty, const char *display __unused) { struct stat st; char ttyname[PATH_MAX]; time_t now; u_long idle; /* * If this is an X terminal or console, then try the * XIdle extension */ #ifdef XIDLE if (display && *display && (idle = XqueryIdle(display)) >= 0) return(idle); #endif idle = 0; if (*tty == 'X') { u_long kbd_idle, mouse_idle; #if !defined(__FreeBSD__) kbd_idle = getidle("kbd", NULL); #else kbd_idle = getidle("vga", NULL); #endif mouse_idle = getidle("mouse", NULL); idle = (kbd_idle < mouse_idle)?kbd_idle:mouse_idle; } else { sprintf(ttyname, "%s/%s", _PATH_DEV, tty); if (stat(ttyname, &st) < 0) { #ifdef DEBUG printf("%s: %s\n", ttyname, strerror(errno)); #endif return(-1); } time(&now); #ifdef DEBUG printf("%s: now=%d atime=%d\n", ttyname, now, st.st_atime); #endif idle = now - st.st_atime; idle = (idle + 30) / 60; /* secs->mins */ } return(idle); } static utmpidlearr * do_names_2(void) { static utmpidlearr ut; struct utmpx *usr; int nusers = 0; memset(&ut, 0, sizeof(ut)); ut.utmpidlearr_val = &utmp_idle[0]; setutxent(); while ((usr = getutxent()) != NULL && nusers < MAXUSERS) { if (usr->ut_type != USER_PROCESS) continue; memcpy(&utmp_list[nusers], usr, sizeof(*usr)); utmp_idle[nusers].ui_utmp.ut_time = usr->ut_tv.tv_sec; utmp_idle[nusers].ui_idle = getidle(usr->ut_line, usr->ut_host); utmp_idle[nusers].ui_utmp.ut_line = utmp_list[nusers].ut_line; utmp_idle[nusers].ui_utmp.ut_name = utmp_list[nusers].ut_user; utmp_idle[nusers].ui_utmp.ut_host = utmp_list[nusers].ut_host; nusers++; } endutxent(); ut.utmpidlearr_len = nusers; return(&ut); } static int * rusers_num(void *argp __unused, struct svc_req *rqstp __unused) { static int num_users = 0; struct utmpx *usr; setutxent(); while ((usr = getutxent()) != NULL) { if (usr->ut_type != USER_PROCESS) continue; num_users++; } endutxent(); return(&num_users); } static utmparr * do_names_1(void) { utmpidlearr *utidle; static utmparr ut; unsigned int i; bzero((char *)&ut, sizeof(ut)); utidle = do_names_2(); if (utidle) { ut.utmparr_len = utidle->utmpidlearr_len; ut.utmparr_val = &old_utmp[0]; for (i = 0; i < ut.utmparr_len; i++) bcopy(&utmp_idle[i].ui_utmp, &old_utmp[i], sizeof(old_utmp[0])); } return(&ut); } utmpidlearr * rusersproc_names_2_svc(void *argp __unused, struct svc_req *rqstp __unused) { return (do_names_2()); } utmpidlearr * rusersproc_allnames_2_svc(void *argp __unused, struct svc_req *rqstp __unused) { return (do_names_2()); } utmparr * rusersproc_names_1_svc(void *argp __unused, struct svc_req *rqstp __unused) { return (do_names_1()); } utmparr * rusersproc_allnames_1_svc(void *argp __unused, struct svc_req *rqstp __unused) { return (do_names_1()); } typedef void *(*rusersproc_t)(void *, struct svc_req *); void rusers_service(struct svc_req *rqstp, SVCXPRT *transp) { union { int fill; } argument; char *result; xdrproc_t xdr_argument, xdr_result; rusersproc_t local; switch (rqstp->rq_proc) { case NULLPROC: (void)svc_sendreply(transp, (xdrproc_t)xdr_void, NULL); goto leave; case RUSERSPROC_NUM: xdr_argument = (xdrproc_t)xdr_void; xdr_result = (xdrproc_t)xdr_int; local = (rusersproc_t)rusers_num; break; case RUSERSPROC_NAMES: xdr_argument = (xdrproc_t)xdr_void; xdr_result = (xdrproc_t)xdr_utmpidlearr; switch (rqstp->rq_vers) { case RUSERSVERS_ORIG: local = (rusersproc_t)rusersproc_names_1_svc; break; case RUSERSVERS_IDLE: local = (rusersproc_t)rusersproc_names_2_svc; break; default: svcerr_progvers(transp, RUSERSVERS_ORIG, RUSERSVERS_IDLE); goto leave; /*NOTREACHED*/ } break; case RUSERSPROC_ALLNAMES: xdr_argument = (xdrproc_t)xdr_void; xdr_result = (xdrproc_t)xdr_utmpidlearr; switch (rqstp->rq_vers) { case RUSERSVERS_ORIG: local = (rusersproc_t)rusersproc_allnames_1_svc; break; case RUSERSVERS_IDLE: local = (rusersproc_t)rusersproc_allnames_2_svc; break; default: svcerr_progvers(transp, RUSERSVERS_ORIG, RUSERSVERS_IDLE); goto leave; /*NOTREACHED*/ } break; default: svcerr_noproc(transp); goto leave; } bzero(&argument, sizeof(argument)); if (!svc_getargs(transp, (xdrproc_t)xdr_argument, &argument)) { svcerr_decode(transp); goto leave; } result = (*local)(&argument, rqstp); if (result != NULL && !svc_sendreply(transp, (xdrproc_t)xdr_result, result)) { svcerr_systemerr(transp); } if (!svc_freeargs(transp, (xdrproc_t)xdr_argument, &argument)) { syslog(LOG_ERR, "unable to free arguments"); exit(1); } leave: if (from_inetd) exit(0); } diff --git a/libexec/rpc.rusersd/rusersd.c b/libexec/rpc.rusersd/rusersd.c index e1e77398c8ff..cf00dd8d181e 100644 --- a/libexec/rpc.rusersd/rusersd.c +++ b/libexec/rpc.rusersd/rusersd.c @@ -1,114 +1,109 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1993, John Brezak * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include "extern.h" int from_inetd = 1; static void cleanup(int sig __unused) { (void) rpcb_unset(RUSERSPROG, RUSERSVERS_IDLE, NULL); (void) rpcb_unset(RUSERSPROG, RUSERSVERS_ORIG, NULL); exit(0); } int main(int argc __unused, char *argv[] __unused) { SVCXPRT *transp = NULL; /* Keep compiler happy. */ int ok; struct sockaddr_storage from; socklen_t fromlen; /* * See if inetd started us */ fromlen = sizeof(from); if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) { from_inetd = 0; } if (!from_inetd) { daemon(0, 0); (void) rpcb_unset(RUSERSPROG, RUSERSVERS_IDLE, NULL); (void) rpcb_unset(RUSERSPROG, RUSERSVERS_ORIG, NULL); (void) signal(SIGINT, cleanup); (void) signal(SIGTERM, cleanup); (void) signal(SIGHUP, cleanup); } openlog("rpc.rusersd", LOG_CONS|LOG_PID, LOG_DAEMON); if (from_inetd) { transp = svc_tli_create(0, NULL, NULL, 0, 0); if (transp == NULL) { syslog(LOG_ERR, "cannot create udp service."); exit(1); } ok = svc_reg(transp, RUSERSPROG, RUSERSVERS_IDLE, rusers_service, NULL); } else ok = svc_create(rusers_service, RUSERSPROG, RUSERSVERS_IDLE, "udp"); if (!ok) { syslog(LOG_ERR, "unable to register (RUSERSPROG, RUSERSVERS_IDLE, %s)", (!from_inetd)?"udp":"(inetd)"); exit(1); } if (from_inetd) ok = svc_reg(transp, RUSERSPROG, RUSERSVERS_ORIG, rusers_service, NULL); else ok = svc_create(rusers_service, RUSERSPROG, RUSERSVERS_ORIG, "udp"); if (!ok) { syslog(LOG_ERR, "unable to register (RUSERSPROG, RUSERSVERS_ORIG, %s)", (!from_inetd)?"udp":"(inetd)"); exit(1); } svc_run(); syslog(LOG_ERR, "svc_run returned"); exit(1); } diff --git a/libexec/rpc.sprayd/sprayd.c b/libexec/rpc.sprayd/sprayd.c index 209d74af59af..2a71a93bf4ef 100644 --- a/libexec/rpc.sprayd/sprayd.c +++ b/libexec/rpc.sprayd/sprayd.c @@ -1,165 +1,160 @@ /* $NetBSD: sprayd.c,v 1.15 2009/10/21 01:07:46 snj Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 1994 Christos Zoulas * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include static void spray_service(struct svc_req *, SVCXPRT *); static int from_inetd = 1; #define timersub(tvp, uvp, vvp) \ do { \ (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ if ((vvp)->tv_usec < 0) { \ (vvp)->tv_sec--; \ (vvp)->tv_usec += 1000000; \ } \ } while (0) #define TIMEOUT 120 static void cleanup(int sig __unused) { (void)rpcb_unset(SPRAYPROG, SPRAYVERS, NULL); exit(0); } static void die(int sig __unused) { exit(0); } int main(int argc __unused, char *argv[] __unused) { SVCXPRT *transp; int ok; struct sockaddr_storage from; socklen_t fromlen; /* * See if inetd started us */ fromlen = sizeof(from); if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) { from_inetd = 0; } if (!from_inetd) { daemon(0, 0); (void)rpcb_unset(SPRAYPROG, SPRAYVERS, NULL); (void)signal(SIGINT, cleanup); (void)signal(SIGTERM, cleanup); (void)signal(SIGHUP, cleanup); } else { (void)signal(SIGALRM, die); alarm(TIMEOUT); } openlog("rpc.sprayd", LOG_PID, LOG_DAEMON); if (from_inetd) { transp = svc_tli_create(0, NULL, NULL, 0, 0); if (transp == NULL) { syslog(LOG_ERR, "cannot create udp service."); exit(1); } ok = svc_reg(transp, SPRAYPROG, SPRAYVERS, spray_service, NULL); } else ok = svc_create(spray_service, SPRAYPROG, SPRAYVERS, "udp"); if (!ok) { syslog(LOG_ERR, "unable to register (SPRAYPROG, SPRAYVERS, %s)", (!from_inetd)?"udp":"(inetd)"); exit(1); } svc_run(); syslog(LOG_ERR, "svc_run returned"); return 1; } static void spray_service(struct svc_req *rqstp, SVCXPRT *transp) { static spraycumul scum; static struct timeval clear, get; switch (rqstp->rq_proc) { case SPRAYPROC_CLEAR: scum.counter = 0; (void)gettimeofday(&clear, 0); /*FALLTHROUGH*/ case NULLPROC: (void)svc_sendreply(transp, (xdrproc_t)xdr_void, NULL); return; case SPRAYPROC_SPRAY: scum.counter++; return; case SPRAYPROC_GET: (void)gettimeofday(&get, 0); timersub(&get, &clear, &get); scum.clock.sec = get.tv_sec; scum.clock.usec = get.tv_usec; break; default: svcerr_noproc(transp); return; } if (!svc_sendreply(transp, (xdrproc_t)xdr_spraycumul, &scum)) { svcerr_systemerr(transp); syslog(LOG_WARNING, "bad svc_sendreply"); } } diff --git a/libexec/rtld-elf/rtld_malloc.c b/libexec/rtld-elf/rtld_malloc.c index 4b5140551675..dafbc222322e 100644 --- a/libexec/rtld-elf/rtld_malloc.c +++ b/libexec/rtld-elf/rtld_malloc.c @@ -1,326 +1,325 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) /*static char *sccsid = "from: @(#)malloc.c 5.11 (Berkeley) 2/23/91";*/ -static char *rcsid = "$FreeBSD$"; #endif /* LIBC_SCCS and not lint */ /* * malloc.c (Caltech) 2/21/82 * Chris Kingsley, kingsley@cit-20. * * This is a very fast storage allocator. It allocates blocks of a small * number of different sizes, and keeps free lists of each size. Blocks that * don't exactly fit are passed up to the next larger size. In this * implementation, the available sizes are 2^n-4 (or 2^n-10) bytes long. * This is designed for use in a virtual memory environment. */ #include #include #include #include #include #include #include #include #ifdef IN_RTLD #include "rtld.h" #include "rtld_printf.h" #include "rtld_paths.h" #endif #include "rtld_malloc.h" /* * Pre-allocate mmap'ed pages */ #define NPOOLPAGES (128*1024/pagesz) static caddr_t pagepool_start, pagepool_end; /* * The overhead on a block is at least 4 bytes. When free, this space * contains a pointer to the next free block, and the bottom two bits must * be zero. When in use, the first byte is set to MAGIC, and the second * byte is the size index. The remaining bytes are for alignment. */ union overhead { union overhead *ov_next; /* when free */ struct { uint16_t ovu_index; /* bucket # */ uint8_t ovu_magic; /* magic number */ } ovu; #define ov_magic ovu.ovu_magic #define ov_index ovu.ovu_index }; static void morecore(int bucket); static int morepages(int n); #define MAGIC 0xef /* magic # on accounting info */ #define AMAGIC 0xdf /* magic # for aligned alloc */ /* * nextf[i] is the pointer to the next free block of size * (FIRST_BUCKET_SIZE << i). The overhead information precedes the data * area returned to the user. */ #define LOW_BITS 3 #define FIRST_BUCKET_SIZE (1U << LOW_BITS) #define NBUCKETS 30 static union overhead *nextf[NBUCKETS]; static int pagesz; /* page size */ /* * The array of supported page sizes is provided by the user, i.e., the * program that calls this storage allocator. That program must initialize * the array before making its first call to allocate storage. The array * must contain at least one page size. The page sizes must be stored in * increasing order. */ static void * cp2op(void *cp) { return (((caddr_t)cp - sizeof(union overhead))); } void * __crt_malloc(size_t nbytes) { union overhead *op; int bucket; size_t amt; /* * First time malloc is called, setup page size. */ if (pagesz == 0) pagesz = pagesizes[0]; /* * Convert amount of memory requested into closest block size * stored in hash buckets which satisfies request. * Account for space used per block for accounting. */ amt = FIRST_BUCKET_SIZE; bucket = 0; while (nbytes > amt - sizeof(*op)) { amt <<= 1; bucket++; if (amt == 0 || bucket >= NBUCKETS) return (NULL); } /* * If nothing in hash bucket right now, * request more memory from the system. */ if ((op = nextf[bucket]) == NULL) { morecore(bucket); if ((op = nextf[bucket]) == NULL) return (NULL); } /* remove from linked list */ nextf[bucket] = op->ov_next; op->ov_magic = MAGIC; op->ov_index = bucket; return ((char *)(op + 1)); } void * __crt_calloc(size_t num, size_t size) { void *ret; if (size != 0 && (num * size) / size != num) { /* size_t overflow. */ return (NULL); } if ((ret = __crt_malloc(num * size)) != NULL) memset(ret, 0, num * size); return (ret); } void * __crt_aligned_alloc_offset(size_t align, size_t size, size_t offset) { void *mem, *ov; union overhead ov1; uintptr_t x; if (align < FIRST_BUCKET_SIZE) align = FIRST_BUCKET_SIZE; offset &= align - 1; mem = __crt_malloc(size + align + offset + sizeof(union overhead)); if (mem == NULL) return (NULL); x = roundup2((uintptr_t)mem + sizeof(union overhead), align); x += offset; ov = cp2op((void *)x); ov1.ov_magic = AMAGIC; ov1.ov_index = x - (uintptr_t)mem + sizeof(union overhead); memcpy(ov, &ov1, sizeof(ov1)); return ((void *)x); } /* * Allocate more memory to the indicated bucket. */ static void morecore(int bucket) { union overhead *op; int sz; /* size of desired block */ int amt; /* amount to allocate */ int nblks; /* how many blocks we get */ sz = FIRST_BUCKET_SIZE << bucket; if (sz < pagesz) { amt = pagesz; nblks = amt / sz; } else { amt = sz; nblks = 1; } if (amt > pagepool_end - pagepool_start) if (morepages(amt / pagesz + NPOOLPAGES) == 0 && /* Retry with min required size */ morepages(amt / pagesz) == 0) return; op = (union overhead *)pagepool_start; pagepool_start += amt; /* * Add new memory allocated to that on * free list for this hash bucket. */ nextf[bucket] = op; while (--nblks > 0) { op->ov_next = (union overhead *)((caddr_t)op + sz); op = (union overhead *)((caddr_t)op + sz); } } void __crt_free(void *cp) { union overhead *op, op1; void *opx; int size; if (cp == NULL) return; opx = cp2op(cp); memcpy(&op1, opx, sizeof(op1)); op = op1.ov_magic == AMAGIC ? (void *)((caddr_t)cp - op1.ov_index) : opx; if (op->ov_magic != MAGIC) return; /* sanity */ size = op->ov_index; op->ov_next = nextf[size]; /* also clobbers ov_magic */ nextf[size] = op; } void * __crt_realloc(void *cp, size_t nbytes) { u_int onb; int i; union overhead *op; char *res; if (cp == NULL) return (__crt_malloc(nbytes)); op = cp2op(cp); if (op->ov_magic != MAGIC) return (NULL); /* Double-free or bad argument */ i = op->ov_index; onb = 1 << (i + 3); if (onb < (u_int)pagesz) onb -= sizeof(*op); else onb += pagesz - sizeof(*op); /* avoid the copy if same size block */ if (i != 0) { i = 1 << (i + 2); if (i < pagesz) i -= sizeof(*op); else i += pagesz - sizeof(*op); } if (nbytes <= onb && nbytes > (size_t)i) return (cp); if ((res = __crt_malloc(nbytes)) == NULL) return (NULL); bcopy(cp, res, (nbytes < onb) ? nbytes : onb); __crt_free(cp); return (res); } static int morepages(int n) { caddr_t addr; int offset; if (pagepool_end - pagepool_start > pagesz) { addr = roundup2(pagepool_start, pagesz); if (munmap(addr, pagepool_end - addr) != 0) { #ifdef IN_RTLD rtld_fdprintf(STDERR_FILENO, _BASENAME_RTLD ": " "morepages: cannot munmap %p: %s\n", addr, rtld_strerror(errno)); #endif } } offset = (uintptr_t)pagepool_start - rounddown2( (uintptr_t)pagepool_start, pagesz); addr = mmap(0, n * pagesz, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); if (addr == MAP_FAILED) { #ifdef IN_RTLD rtld_fdprintf(STDERR_FILENO, _BASENAME_RTLD ": morepages: " "cannot mmap anonymous memory: %s\n", rtld_strerror(errno)); #endif pagepool_start = pagepool_end = NULL; return (0); } pagepool_start = addr; pagepool_end = pagepool_start + n * pagesz; pagepool_start += offset; return (n); } diff --git a/libexec/talkd/announce.c b/libexec/talkd/announce.c index 9cf326669998..b1b1acc09553 100644 --- a/libexec/talkd/announce.c +++ b/libexec/talkd/announce.c @@ -1,167 +1,165 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)announce.c 8.3 (Berkeley) 4/28/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ttymsg.h" #include "extern.h" /* * Announce an invitation to talk. */ /* * See if the user is accepting messages. If so, announce that * a talk is requested. */ int announce(CTL_MSG *request, const char *remote_machine) { char full_tty[32]; struct stat stbuf; (void)snprintf(full_tty, sizeof(full_tty), "%s%s", _PATH_DEV, request->r_tty); if (stat(full_tty, &stbuf) < 0 || (stbuf.st_mode&020) == 0) return (PERMISSION_DENIED); return (print_mesg(request->r_tty, request, remote_machine)); } #define max(a,b) ( (a) > (b) ? (a) : (b) ) #define N_LINES 5 #define N_CHARS 256 /* * Build a block of characters containing the message. * It is sent blank filled and in a single block to * try to keep the message in one piece if the recipient * in vi at the time */ int print_mesg(const char *tty, CTL_MSG *request, const char *remote_machine) { struct timeval now; time_t clock_sec; struct tm *localclock; struct iovec iovec; char line_buf[N_LINES][N_CHARS]; int sizes[N_LINES]; char big_buf[N_LINES*N_CHARS]; char *bptr, *lptr, *vis_user; int i, j, max_size; i = 0; max_size = 0; gettimeofday(&now, NULL); clock_sec = now.tv_sec; localclock = localtime(&clock_sec); (void)snprintf(line_buf[i], N_CHARS, " "); sizes[i] = strlen(line_buf[i]); max_size = max(max_size, sizes[i]); i++; (void)snprintf(line_buf[i], N_CHARS, "Message from Talk_Daemon@%s at %d:%02d on %d/%.2d/%.2d ...", hostname, localclock->tm_hour , localclock->tm_min, localclock->tm_year + 1900, localclock->tm_mon + 1, localclock->tm_mday); sizes[i] = strlen(line_buf[i]); max_size = max(max_size, sizes[i]); i++; vis_user = malloc(strlen(request->l_name) * 4 + 1); strvis(vis_user, request->l_name, VIS_CSTYLE); (void)snprintf(line_buf[i], N_CHARS, "talk: connection requested by %s@%s", vis_user, remote_machine); sizes[i] = strlen(line_buf[i]); max_size = max(max_size, sizes[i]); i++; (void)snprintf(line_buf[i], N_CHARS, "talk: respond with: talk %s@%s", vis_user, remote_machine); sizes[i] = strlen(line_buf[i]); max_size = max(max_size, sizes[i]); i++; (void)snprintf(line_buf[i], N_CHARS, " "); sizes[i] = strlen(line_buf[i]); max_size = max(max_size, sizes[i]); i++; bptr = big_buf; *bptr++ = '\007'; /* send something to wake them up */ *bptr++ = '\r'; /* add a \r in case of raw mode */ *bptr++ = '\n'; for (i = 0; i < N_LINES; i++) { /* copy the line into the big buffer */ lptr = line_buf[i]; while (*lptr != '\0') *(bptr++) = *(lptr++); /* pad out the rest of the lines with blanks */ for (j = sizes[i]; j < max_size + 2; j++) *(bptr++) = ' '; *(bptr++) = '\r'; /* add a \r in case of raw mode */ *(bptr++) = '\n'; } *bptr = '\0'; iovec.iov_base = big_buf; iovec.iov_len = bptr - big_buf; /* * we choose a timeout of RING_WAIT-5 seconds so that we don't * stack up processes trying to write messages to a tty * that is permanently blocked. */ if (ttymsg(&iovec, 1, tty, RING_WAIT - 5) != NULL) return (FAILED); return (SUCCESS); } diff --git a/libexec/talkd/print.c b/libexec/talkd/print.c index 4d10329dd7f8..47ccb89f20d6 100644 --- a/libexec/talkd/print.c +++ b/libexec/talkd/print.c @@ -1,91 +1,89 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)print.c 8.1 (Berkeley) 6/4/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ /* debug print routines */ #include #include #include #include #include #include #include "extern.h" static const char *types[] = { "leave_invite", "look_up", "delete", "announce" }; #define NTYPES (sizeof (types) / sizeof (types[0])) static const char *answers[] = { "success", "not_here", "failed", "machine_unknown", "permission_denied", "unknown_request", "badversion", "badaddr", "badctladdr" }; #define NANSWERS (sizeof (answers) / sizeof (answers[0])) void print_request(const char *cp, CTL_MSG *mp) { const char *tp; char tbuf[80]; if (mp->type > NTYPES) { (void)snprintf(tbuf, sizeof(tbuf), "type %d", mp->type); tp = tbuf; } else tp = types[mp->type]; syslog(LOG_DEBUG, "%s: %s: id %lu, l_user %s, r_user %s, r_tty %s", cp, tp, (long)mp->id_num, mp->l_name, mp->r_name, mp->r_tty); } void print_response(const char *cp, CTL_RESPONSE *rp) { const char *tp, *ap; char tbuf[80], abuf[80]; if (rp->type > NTYPES) { (void)snprintf(tbuf, sizeof(tbuf), "type %d", rp->type); tp = tbuf; } else tp = types[rp->type]; if (rp->answer > NANSWERS) { (void)snprintf(abuf, sizeof(abuf), "answer %d", rp->answer); ap = abuf; } else ap = answers[rp->answer]; syslog(LOG_DEBUG, "%s: %s: %s, id %d", cp, tp, ap, ntohl(rp->id_num)); } diff --git a/libexec/talkd/process.c b/libexec/talkd/process.c index 2bd22a1becb2..886fc038ab97 100644 --- a/libexec/talkd/process.c +++ b/libexec/talkd/process.c @@ -1,223 +1,221 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)process.c 8.2 (Berkeley) 11/16/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ /* * process.c handles the requests, which can be of three types: * ANNOUNCE - announce to a user that a talk is wanted * LEAVE_INVITE - insert the request into the table * LOOK_UP - look up to see if a request is waiting in * in the table for the local user * DELETE - delete invitation */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "extern.h" void process_request(CTL_MSG *mp, CTL_RESPONSE *rp) { CTL_MSG *ptr; char *s; rp->vers = TALK_VERSION; rp->type = mp->type; rp->id_num = htonl(0); if (mp->vers != TALK_VERSION) { syslog(LOG_WARNING, "bad protocol version %d", mp->vers); rp->answer = BADVERSION; return; } mp->id_num = ntohl(mp->id_num); mp->addr.sa_family = ntohs(mp->addr.sa_family); if (mp->addr.sa_family != AF_INET) { syslog(LOG_WARNING, "bad address, family %d", mp->addr.sa_family); rp->answer = BADADDR; return; } mp->ctl_addr.sa_family = ntohs(mp->ctl_addr.sa_family); if (mp->ctl_addr.sa_family != AF_INET) { syslog(LOG_WARNING, "bad control address, family %d", mp->ctl_addr.sa_family); rp->answer = BADCTLADDR; return; } for (s = mp->l_name; *s; s++) if (!isprint(*s)) { syslog(LOG_NOTICE, "illegal user name. Aborting"); rp->answer = FAILED; return; } mp->pid = ntohl(mp->pid); if (debug) print_request("process_request", mp); switch (mp->type) { case ANNOUNCE: do_announce(mp, rp); break; case LEAVE_INVITE: ptr = find_request(mp); if (ptr != (CTL_MSG *)0) { rp->id_num = htonl(ptr->id_num); rp->answer = SUCCESS; } else insert_table(mp, rp); break; case LOOK_UP: ptr = find_match(mp); if (ptr != (CTL_MSG *)0) { rp->id_num = htonl(ptr->id_num); rp->addr = ptr->addr; rp->addr.sa_family = htons(ptr->addr.sa_family); rp->answer = SUCCESS; } else rp->answer = NOT_HERE; break; case DELETE: rp->answer = delete_invite(mp->id_num); break; default: rp->answer = UNKNOWN_REQUEST; break; } if (debug) print_response("process_request", rp); } void do_announce(CTL_MSG *mp, CTL_RESPONSE *rp) { struct hostent *hp; CTL_MSG *ptr; int result; /* see if the user is logged */ result = find_user(mp->r_name, mp->r_tty); if (result != SUCCESS) { rp->answer = result; return; } #define satosin(sa) ((struct sockaddr_in *)(void *)(sa)) hp = gethostbyaddr(&satosin(&mp->ctl_addr)->sin_addr, sizeof (struct in_addr), AF_INET); if (hp == (struct hostent *)0) { rp->answer = MACHINE_UNKNOWN; return; } ptr = find_request(mp); if (ptr == (CTL_MSG *) 0) { insert_table(mp, rp); rp->answer = announce(mp, hp->h_name); return; } if (mp->id_num > ptr->id_num) { /* * This is an explicit re-announce, so update the id_num * field to avoid duplicates and re-announce the talk. */ ptr->id_num = new_id(); rp->id_num = htonl(ptr->id_num); rp->answer = announce(mp, hp->h_name); } else { /* a duplicated request, so ignore it */ rp->id_num = htonl(ptr->id_num); rp->answer = SUCCESS; } } /* * Search utmp for the local user */ int find_user(const char *name, char *tty) { struct utmpx *ut; int status; struct stat statb; time_t best = 0; char ftty[sizeof(_PATH_DEV) - 1 + sizeof(ut->ut_line)]; setutxent(); status = NOT_HERE; (void) strcpy(ftty, _PATH_DEV); while ((ut = getutxent()) != NULL) if (ut->ut_type == USER_PROCESS && strcmp(ut->ut_user, name) == 0) { if (*tty == '\0' || best != 0) { if (best == 0) status = PERMISSION_DENIED; /* no particular tty was requested */ (void) strcpy(ftty + sizeof(_PATH_DEV) - 1, ut->ut_line); if (stat(ftty, &statb) == 0) { if (!(statb.st_mode & 020)) continue; if (statb.st_atime > best) { best = statb.st_atime; (void) strcpy(tty, ut->ut_line); status = SUCCESS; continue; } } } if (strcmp(ut->ut_line, tty) == 0) { status = SUCCESS; break; } } endutxent(); return (status); } diff --git a/libexec/talkd/table.c b/libexec/talkd/table.c index ed1560f86ff8..495d63b02b06 100644 --- a/libexec/talkd/table.c +++ b/libexec/talkd/table.c @@ -1,235 +1,233 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)table.c 8.1 (Berkeley) 6/4/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ /* * Routines to handle insertion, deletion, etc on the table * of requests kept by the daemon. Nothing fancy here, linear * search on a double-linked list. A time is kept with each * entry so that overly old invitations can be eliminated. * * Consider this a mis-guided attempt at modularity */ #include #include #include #include #include #include #include #include #include #include #include "extern.h" #define MAX_ID 16000 /* << 2^15 so I don't have sign troubles */ #define NIL ((TABLE_ENTRY *)0) static struct timespec ts; typedef struct table_entry TABLE_ENTRY; struct table_entry { CTL_MSG request; long time; TABLE_ENTRY *next; TABLE_ENTRY *last; }; static void delete(TABLE_ENTRY *); static TABLE_ENTRY *table = NIL; /* * Look in the table for an invitation that matches the current * request looking for an invitation */ CTL_MSG * find_match(CTL_MSG *request) { TABLE_ENTRY *ptr, *next; time_t current_time; clock_gettime(CLOCK_MONOTONIC_FAST, &ts); current_time = ts.tv_sec; if (debug) print_request("find_match", request); for (ptr = table; ptr != NIL; ptr = next) { next = ptr->next; if ((ptr->time - current_time) > MAX_LIFE) { /* the entry is too old */ if (debug) print_request("deleting expired entry", &ptr->request); delete(ptr); continue; } if (debug) print_request("", &ptr->request); if (strcmp(request->l_name, ptr->request.r_name) == 0 && strcmp(request->r_name, ptr->request.l_name) == 0 && ptr->request.type == LEAVE_INVITE) return (&ptr->request); } return ((CTL_MSG *)0); } /* * Look for an identical request, as opposed to a complimentary * one as find_match does */ CTL_MSG * find_request(CTL_MSG *request) { TABLE_ENTRY *ptr, *next; time_t current_time; clock_gettime(CLOCK_MONOTONIC_FAST, &ts); current_time = ts.tv_sec; /* * See if this is a repeated message, and check for * out of date entries in the table while we are it. */ if (debug) print_request("find_request", request); for (ptr = table; ptr != NIL; ptr = next) { next = ptr->next; if ((ptr->time - current_time) > MAX_LIFE) { /* the entry is too old */ if (debug) print_request("deleting expired entry", &ptr->request); delete(ptr); continue; } if (debug) print_request("", &ptr->request); if (strcmp(request->r_name, ptr->request.r_name) == 0 && strcmp(request->l_name, ptr->request.l_name) == 0 && request->type == ptr->request.type && request->pid == ptr->request.pid) { /* update the time if we 'touch' it */ ptr->time = current_time; return (&ptr->request); } } return ((CTL_MSG *)0); } void insert_table(CTL_MSG *request, CTL_RESPONSE *response) { TABLE_ENTRY *ptr; time_t current_time; clock_gettime(CLOCK_MONOTONIC_FAST, &ts); current_time = ts.tv_sec; request->id_num = new_id(); response->id_num = htonl(request->id_num); /* insert a new entry into the top of the list */ ptr = (TABLE_ENTRY *)malloc(sizeof(TABLE_ENTRY)); if (ptr == NIL) { syslog(LOG_ERR, "insert_table: Out of memory"); _exit(1); } ptr->time = current_time; ptr->request = *request; ptr->next = table; if (ptr->next != NIL) ptr->next->last = ptr; ptr->last = NIL; table = ptr; } /* * Generate a unique non-zero sequence number */ int new_id(void) { static int current_id = 0; current_id = (current_id + 1) % MAX_ID; /* 0 is reserved, helps to pick up bugs */ if (current_id == 0) current_id = 1; return (current_id); } /* * Delete the invitation with id 'id_num' */ int delete_invite(u_int32_t id_num) { TABLE_ENTRY *ptr; if (debug) syslog(LOG_DEBUG, "delete_invite(%d)", id_num); for (ptr = table; ptr != NIL; ptr = ptr->next) { if (ptr->request.id_num == id_num) break; if (debug) print_request("", &ptr->request); } if (ptr != NIL) { delete(ptr); return (SUCCESS); } return (NOT_HERE); } /* * Classic delete from a double-linked list */ static void delete(TABLE_ENTRY *ptr) { if (debug) print_request("delete", &ptr->request); if (table == ptr) table = ptr->next; else if (ptr->last != NIL) ptr->last->next = ptr->next; if (ptr->next != NIL) ptr->next->last = ptr->last; free((char *)ptr); } diff --git a/libexec/talkd/talkd.c b/libexec/talkd/talkd.c index eb609207b156..76d0ec5a09ad 100644 --- a/libexec/talkd/talkd.c +++ b/libexec/talkd/talkd.c @@ -1,139 +1,137 @@ /* * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1983, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)talkd.c 8.1 (Berkeley) 6/4/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ /* * The top level of the daemon, the format is heavily borrowed * from rwhod.c. Basically: find out who and where you are; * disconnect all descriptors and ttys, and then endless * loop on waiting for and processing requests */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "extern.h" static CTL_MSG request; static CTL_RESPONSE response; int debug = 0; static long lastmsgtime; char hostname[MAXHOSTNAMELEN]; #define TIMEOUT 30 #define MAXIDLE 120 int main(int argc, char *argv[]) { register CTL_MSG *mp = &request; int cc; struct sockaddr ctl_addr; #ifdef NOTDEF /* * removed so ntalkd can run in tty sandbox */ if (getuid()) errx(1, "getuid: not super-user"); #endif openlog("talkd", LOG_PID, LOG_DAEMON); if (gethostname(hostname, sizeof(hostname) - 1) < 0) { syslog(LOG_ERR, "gethostname: %m"); _exit(1); } hostname[sizeof(hostname) - 1] = '\0'; if (chdir(_PATH_DEV) < 0) { syslog(LOG_ERR, "chdir: %s: %m", _PATH_DEV); _exit(1); } if (argc > 1 && strcmp(argv[1], "-d") == 0) debug = 1; signal(SIGALRM, timeout); alarm(TIMEOUT); for (;;) { cc = recv(0, (char *)mp, sizeof(*mp), 0); if (cc != sizeof (*mp)) { if (cc < 0 && errno != EINTR) syslog(LOG_WARNING, "recv: %m"); continue; } lastmsgtime = time(0); (void)memcpy(&ctl_addr.sa_data, &mp->ctl_addr.sa_data, sizeof(ctl_addr.sa_data)); ctl_addr.sa_family = ntohs(mp->ctl_addr.sa_family); ctl_addr.sa_len = sizeof(ctl_addr); process_request(mp, &response); /* can block here, is this what I want? */ cc = sendto(STDIN_FILENO, (char *)&response, sizeof(response), 0, &ctl_addr, sizeof(ctl_addr)); if (cc != sizeof (response)) syslog(LOG_WARNING, "sendto: %m"); } } void timeout(int sig __unused) { int save_errno = errno; if (time(0) - lastmsgtime >= MAXIDLE) _exit(0); alarm(TIMEOUT); errno = save_errno; } diff --git a/sbin/dump/dumprmt.c b/sbin/dump/dumprmt.c index d1769faa3daf..a8b2d9c221da 100644 --- a/sbin/dump/dumprmt.c +++ b/sbin/dump/dumprmt.c @@ -1,376 +1,374 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)dumprmt.c 8.3 (Berkeley) 4/28/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pathnames.h" #include "dump.h" #define TS_CLOSED 0 #define TS_OPEN 1 static int rmtstate = TS_CLOSED; static int rmtape; static char *rmtpeer; static int okname(const char *); static int rmtcall(const char *, const char *); static void rmtconnaborted(int); static int rmtgetb(void); static void rmtgetconn(void); static void rmtgets(char *, int); static int rmtreply(const char *); static int errfd = -1; int rmthost(const char *host) { rmtpeer = strdup(host); if (rmtpeer == NULL) return (0); signal(SIGPIPE, rmtconnaborted); rmtgetconn(); if (rmtape < 0) return (0); return (1); } static void rmtconnaborted(int sig __unused) { msg("Lost connection to remote host.\n"); if (errfd != -1) { fd_set r; struct timeval t; FD_ZERO(&r); FD_SET(errfd, &r); t.tv_sec = 0; t.tv_usec = 0; if (select(errfd + 1, &r, NULL, NULL, &t)) { int i; char buf[2048]; if ((i = read(errfd, buf, sizeof(buf) - 1)) > 0) { buf[i] = '\0'; msg("on %s: %s%s", rmtpeer, buf, buf[i - 1] == '\n' ? "" : "\n"); } } } exit(X_ABORT); } void rmtgetconn(void) { char *cp; const char *rmt; static struct servent *sp = NULL; static struct passwd *pwd = NULL; char *tuser; int size; int throughput; int on; if (sp == NULL) { sp = getservbyname("shell", "tcp"); if (sp == NULL) { msg("shell/tcp: unknown service\n"); exit(X_STARTUP); } pwd = getpwuid(getuid()); if (pwd == NULL) { msg("who are you?\n"); exit(X_STARTUP); } } if ((cp = strchr(rmtpeer, '@')) != NULL) { tuser = rmtpeer; *cp = '\0'; if (!okname(tuser)) exit(X_STARTUP); rmtpeer = ++cp; } else tuser = pwd->pw_name; if ((rmt = getenv("RMT")) == NULL) rmt = _PATH_RMT; msg("%s", ""); rmtape = rcmd(&rmtpeer, (u_short)sp->s_port, pwd->pw_name, tuser, rmt, &errfd); if (rmtape < 0) { msg("login to %s as %s failed.\n", rmtpeer, tuser); return; } (void)fprintf(stderr, "Connection to %s established.\n", rmtpeer); size = ntrec * TP_BSIZE; if (size > 60 * 1024) /* XXX */ size = 60 * 1024; /* Leave some space for rmt request/response protocol */ size += 2 * 1024; while (size > TP_BSIZE && setsockopt(rmtape, SOL_SOCKET, SO_SNDBUF, &size, sizeof (size)) < 0) size -= TP_BSIZE; (void)setsockopt(rmtape, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size)); throughput = IPTOS_THROUGHPUT; if (setsockopt(rmtape, IPPROTO_IP, IP_TOS, &throughput, sizeof(throughput)) < 0) perror("IP_TOS:IPTOS_THROUGHPUT setsockopt"); on = 1; if (setsockopt(rmtape, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)) < 0) perror("TCP_NODELAY setsockopt"); } static int okname(const char *cp0) { const char *cp; int c; for (cp = cp0; *cp; cp++) { c = *cp; if (!isascii(c) || !(isalnum(c) || c == '_' || c == '-')) { msg("invalid user name %s\n", cp0); return (0); } } return (1); } int rmtopen(const char *tape, int mode) { char buf[256]; (void)snprintf(buf, sizeof (buf), "O%.226s\n%d\n", tape, mode); rmtstate = TS_OPEN; return (rmtcall(tape, buf)); } void rmtclose(void) { if (rmtstate != TS_OPEN) return; rmtcall("close", "C\n"); rmtstate = TS_CLOSED; } int rmtread(char *buf, int count) { char line[30]; int n, i, cc; (void)snprintf(line, sizeof (line), "R%d\n", count); n = rmtcall("read", line); if (n < 0) /* rmtcall() properly sets errno for us on errors. */ return (n); for (i = 0; i < n; i += cc) { cc = read(rmtape, buf+i, n - i); if (cc <= 0) rmtconnaborted(0); } return (n); } int rmtwrite(const char *buf, int count) { char line[30]; (void)snprintf(line, sizeof (line), "W%d\n", count); write(rmtape, line, strlen(line)); write(rmtape, buf, count); return (rmtreply("write")); } void rmtwrite0(int count) { char line[30]; (void)snprintf(line, sizeof (line), "W%d\n", count); write(rmtape, line, strlen(line)); } void rmtwrite1(const char *buf, int count) { write(rmtape, buf, count); } int rmtwrite2(void) { return (rmtreply("write")); } int rmtseek(int offset, int pos) /* XXX off_t ? */ { char line[80]; (void)snprintf(line, sizeof (line), "L%d\n%d\n", offset, pos); return (rmtcall("seek", line)); } struct mtget mts; struct mtget * rmtstatus(void) { int i; char *cp; if (rmtstate != TS_OPEN) return (NULL); rmtcall("status", "S\n"); for (i = 0, cp = (char *)&mts; i < sizeof(mts); i++) *cp++ = rmtgetb(); return (&mts); } int rmtioctl(int cmd, int count) { char buf[256]; if (count < 0) return (-1); (void)snprintf(buf, sizeof (buf), "I%d\n%d\n", cmd, count); return (rmtcall("ioctl", buf)); } static int rmtcall(const char *cmd, const char *buf) { if (write(rmtape, buf, strlen(buf)) != strlen(buf)) rmtconnaborted(0); return (rmtreply(cmd)); } static int rmtreply(const char *cmd) { char *cp; char code[30], emsg[BUFSIZ]; rmtgets(code, sizeof (code)); if (*code == 'E' || *code == 'F') { rmtgets(emsg, sizeof (emsg)); msg("%s: %s", cmd, emsg); errno = atoi(code + 1); if (*code == 'F') rmtstate = TS_CLOSED; return (-1); } if (*code != 'A') { /* Kill trailing newline */ cp = code + strlen(code); if (cp > code && *--cp == '\n') *cp = '\0'; msg("Protocol to remote tape server botched (code \"%s\").\n", code); rmtconnaborted(0); } return (atoi(code + 1)); } int rmtgetb(void) { char c; if (read(rmtape, &c, 1) != 1) rmtconnaborted(0); return (c); } /* Get a line (guaranteed to have a trailing newline). */ void rmtgets(char *line, int len) { char *cp = line; while (len > 1) { *cp = rmtgetb(); if (*cp == '\n') { cp[1] = '\0'; return; } cp++; len--; } *cp = '\0'; msg("Protocol to remote tape server botched.\n"); msg("(rmtgets got \"%s\").\n", line); rmtconnaborted(0); } diff --git a/sbin/dump/itime.c b/sbin/dump/itime.c index cb6d55625e6d..d9121e4df05a 100644 --- a/sbin/dump/itime.c +++ b/sbin/dump/itime.c @@ -1,268 +1,266 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)itime.c 8.1 (Berkeley) 6/5/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include "dump.h" struct dumptime { struct dumpdates dt_value; SLIST_ENTRY(dumptime) dt_list; }; SLIST_HEAD(dthead, dumptime) dthead = SLIST_HEAD_INITIALIZER(dthead); int nddates = 0; /* number of records (might be zero) */ struct dumpdates **ddatev; /* the arrayfied version */ char *dumpdates; /* name of the file containing dump date info */ int lastlevel; /* dump level of previous dump */ static void dumprecout(FILE *, const struct dumpdates *); static int getrecord(FILE *, struct dumpdates *); static int makedumpdate(struct dumpdates *, const char *); static void readdumptimes(FILE *); void initdumptimes(void) { FILE *df; if ((df = fopen(dumpdates, "r")) == NULL) { if (errno != ENOENT) { msg("WARNING: cannot read %s: %s\n", dumpdates, strerror(errno)); return; } /* * Dumpdates does not exist, make an empty one. */ msg("WARNING: no file `%s', making an empty one\n", dumpdates); if ((df = fopen(dumpdates, "w")) == NULL) { msg("WARNING: cannot create %s: %s\n", dumpdates, strerror(errno)); return; } (void) fclose(df); if ((df = fopen(dumpdates, "r")) == NULL) { quit("cannot read %s even after creating it: %s\n", dumpdates, strerror(errno)); /* NOTREACHED */ } } (void) flock(fileno(df), LOCK_SH); readdumptimes(df); (void) fclose(df); } static void readdumptimes(FILE *df) { int i; struct dumptime *dtwalk; for (;;) { dtwalk = (struct dumptime *)calloc(1, sizeof (struct dumptime)); if (getrecord(df, &(dtwalk->dt_value)) < 0) { free(dtwalk); break; } nddates++; SLIST_INSERT_HEAD(&dthead, dtwalk, dt_list); } /* * arrayify the list, leaving enough room for the additional * record that we may have to add to the ddate structure */ ddatev = calloc((unsigned) (nddates + 1), sizeof (struct dumpdates *)); dtwalk = SLIST_FIRST(&dthead); for (i = nddates - 1; i >= 0; i--, dtwalk = SLIST_NEXT(dtwalk, dt_list)) ddatev[i] = &dtwalk->dt_value; } void getdumptime(void) { struct dumpdates *ddp; int i; char *fname; fname = disk; #ifdef FDEBUG msg("Looking for name %s in dumpdates = %s for level = %d\n", fname, dumpdates, level); #endif spcl.c_ddate = 0; lastlevel = 0; initdumptimes(); /* * Go find the entry with the same name for a lower increment * and older date */ ITITERATE(i, ddp) { if (strncmp(fname, ddp->dd_name, sizeof (ddp->dd_name)) != 0) continue; if (ddp->dd_level >= level) continue; if (ddp->dd_ddate <= _time64_to_time(spcl.c_ddate)) continue; spcl.c_ddate = _time_to_time64(ddp->dd_ddate); lastlevel = ddp->dd_level; } } void putdumptime(void) { FILE *df; struct dumpdates *dtwalk; int i; int fd; char *fname; char *tmsg; if(uflag == 0) return; if ((df = fopen(dumpdates, "r+")) == NULL) quit("cannot rewrite %s: %s\n", dumpdates, strerror(errno)); fd = fileno(df); (void) flock(fd, LOCK_EX); fname = disk; free(ddatev); ddatev = NULL; nddates = 0; readdumptimes(df); if (fseek(df, 0L, 0) < 0) quit("fseek: %s\n", strerror(errno)); spcl.c_ddate = 0; ITITERATE(i, dtwalk) { if (strncmp(fname, dtwalk->dd_name, sizeof (dtwalk->dd_name)) != 0) continue; if (dtwalk->dd_level != level) continue; goto found; } /* * construct the new upper bound; * Enough room has been allocated. */ dtwalk = ddatev[nddates] = (struct dumpdates *)calloc(1, sizeof (struct dumpdates)); nddates += 1; found: (void) strncpy(dtwalk->dd_name, fname, sizeof (dtwalk->dd_name)); dtwalk->dd_level = level; dtwalk->dd_ddate = _time64_to_time(spcl.c_date); ITITERATE(i, dtwalk) { dumprecout(df, dtwalk); } if (fflush(df)) quit("%s: %s\n", dumpdates, strerror(errno)); if (ftruncate(fd, ftell(df))) quit("ftruncate (%s): %s\n", dumpdates, strerror(errno)); (void) fclose(df); if (spcl.c_date == 0) { tmsg = "the epoch\n"; } else { time_t t = _time64_to_time(spcl.c_date); tmsg = ctime(&t); } msg("level %d dump on %s", level, tmsg); } static void dumprecout(FILE *file, const struct dumpdates *what) { if (strlen(what->dd_name) > DUMPFMTLEN) quit("Name '%s' exceeds DUMPFMTLEN (%d) bytes\n", what->dd_name, DUMPFMTLEN); if (fprintf(file, DUMPOUTFMT, DUMPFMTLEN, what->dd_name, what->dd_level, ctime(&what->dd_ddate)) < 0) quit("%s: %s\n", dumpdates, strerror(errno)); } int recno; static int getrecord(FILE *df, struct dumpdates *ddatep) { char tbuf[BUFSIZ]; recno = 0; if ( (fgets(tbuf, sizeof (tbuf), df)) != tbuf) return(-1); recno++; if (makedumpdate(ddatep, tbuf) < 0) msg("Unknown intermediate format in %s, line %d\n", dumpdates, recno); #ifdef FDEBUG msg("getrecord: %s %d %s", ddatep->dd_name, ddatep->dd_level, ddatep->dd_ddate == 0 ? "the epoch\n" : ctime(&ddatep->dd_ddate)); #endif return(0); } static int makedumpdate(struct dumpdates *ddp, const char *tbuf) { char un_buf[128]; (void) sscanf(tbuf, DUMPINFMT, ddp->dd_name, &ddp->dd_level, un_buf); ddp->dd_ddate = unctime(un_buf); if (ddp->dd_ddate < 0) return(-1); return(0); } diff --git a/sbin/dump/main.c b/sbin/dump/main.c index 779db5fb4b43..a5b4eaa6f8ac 100644 --- a/sbin/dump/main.c +++ b/sbin/dump/main.c @@ -1,797 +1,795 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1980, 1991, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/1/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dump.h" #include "pathnames.h" int mapsize; /* size of the state maps */ char *usedinomap; /* map of allocated inodes */ char *dumpdirmap; /* map of directories to be dumped */ char *dumpinomap; /* map of files to be dumped */ char *disk; /* name of the disk file */ char *tape; /* name of the tape file */ char *popenout; /* popen(3) per-"tape" command */ int level; /* dump level of this dump */ int uflag; /* update flag */ int diskfd; /* disk file descriptor */ int pipeout; /* true => output to standard output */ int density = 0; /* density in bytes/0.1" " <- this is for hilit19 */ long tapesize; /* estimated tape size, blocks */ long tsize; /* tape size in 0.1" units */ int etapes; /* estimated number of tapes */ int nonodump; /* if set, do not honor UF_NODUMP user flags */ int unlimited; /* if set, write to end of medium */ int cachesize = 0; /* block cache size (in bytes), defaults to 0 */ int rsync_friendly; /* be friendly with rsync */ int notify = 0; /* notify operator flag */ int blockswritten = 0; /* number of blocks written on current tape */ int tapeno = 0; /* current tape number */ int ntrec = NTREC; /* # tape blocks in each tape record */ long blocksperfile; /* number of blocks per output file */ int cartridge = 0; /* Assume non-cartridge tape */ char *host = NULL; /* remote host (if any) */ time_t tstart_writing; /* when started writing the first tape block */ time_t tend_writing; /* after writing the last tape block */ int passno; /* current dump pass number */ struct fs *sblock; /* the file system super block */ long dev_bsize = 1; /* recalculated below */ int dev_bshift; /* log2(dev_bsize) */ int tp_bshift; /* log2(TP_BSIZE) */ int snapdump = 0; /* dumping live filesystem, so use snapshot */ static char *getmntpt(char *, int *); static long numarg(const char *, long, long); static void obsolete(int *, char **[]); static void usage(void) __dead2; int main(int argc, char *argv[]) { struct stat sb; ino_t ino; int dirty; union dinode *dp; struct fstab *dt; char *map, *mntpt; int ch, mode, mntflags; int i, ret, anydirskipped, bflag = 0, Tflag = 0, honorlevel = 1; int just_estimate = 0; ino_t maxino; char *tmsg; spcl.c_date = _time_to_time64(time(NULL)); tsize = 0; /* Default later, based on 'c' option for cart tapes */ dumpdates = _PATH_DUMPDATES; popenout = NULL; tape = NULL; if (TP_BSIZE / DEV_BSIZE == 0 || TP_BSIZE % DEV_BSIZE != 0) quit("TP_BSIZE must be a multiple of DEV_BSIZE\n"); level = 0; rsync_friendly = 0; if (argc < 2) usage(); obsolete(&argc, &argv); while ((ch = getopt(argc, argv, "0123456789aB:b:C:cD:d:f:h:LnP:RrSs:T:uWw")) != -1) switch (ch) { /* dump level */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': level = 10 * level + ch - '0'; break; case 'a': /* `auto-size', Write to EOM. */ unlimited = 1; break; case 'B': /* blocks per output file */ blocksperfile = numarg("number of blocks per file", 1L, 0L); break; case 'b': /* blocks per tape write */ ntrec = numarg("number of blocks per write", 1L, 1000L); break; case 'C': cachesize = numarg("cachesize", 0, 0) * 1024 * 1024; break; case 'c': /* Tape is cart. not 9-track */ cartridge = 1; break; case 'D': dumpdates = optarg; break; case 'd': /* density, in bits per inch */ density = numarg("density", 10L, 327670L) / 10; if (density >= 625 && !bflag) ntrec = HIGHDENSITYTREC; break; case 'f': /* output file */ if (popenout != NULL) errx(X_STARTUP, "You cannot use the P and f " "flags together.\n"); tape = optarg; break; case 'h': honorlevel = numarg("honor level", 0L, 10L); break; case 'L': snapdump = 1; break; case 'n': /* notify operators */ notify = 1; break; case 'P': if (tape != NULL) errx(X_STARTUP, "You cannot use the P and f " "flags together.\n"); popenout = optarg; break; case 'r': /* store slightly less data to be friendly to rsync */ if (rsync_friendly < 1) rsync_friendly = 1; break; case 'R': /* store even less data to be friendlier to rsync */ if (rsync_friendly < 2) rsync_friendly = 2; break; case 'S': /* exit after estimating # of tapes */ just_estimate = 1; break; case 's': /* tape size, feet */ tsize = numarg("tape size", 1L, 0L) * 12 * 10; break; case 'T': /* time of last dump */ spcl.c_ddate = unctime(optarg); if (spcl.c_ddate < 0) { (void)fprintf(stderr, "bad time \"%s\"\n", optarg); exit(X_STARTUP); } Tflag = 1; lastlevel = -1; break; case 'u': /* update /etc/dumpdates */ uflag = 1; break; case 'W': /* what to do */ case 'w': lastdump(ch); exit(X_FINOK); /* do nothing else */ default: usage(); } argc -= optind; argv += optind; if (argc < 1) { (void)fprintf(stderr, "Must specify disk or file system\n"); exit(X_STARTUP); } disk = *argv++; argc--; if (argc >= 1) { (void)fprintf(stderr, "Unknown arguments to dump:"); while (argc--) (void)fprintf(stderr, " %s", *argv++); (void)fprintf(stderr, "\n"); exit(X_STARTUP); } if (rsync_friendly && (level > 0)) { (void)fprintf(stderr, "%s %s\n", "rsync friendly options", "can be used only with level 0 dumps."); exit(X_STARTUP); } if (Tflag && uflag) { (void)fprintf(stderr, "You cannot use the T and u flags together.\n"); exit(X_STARTUP); } if (popenout) { tape = "child pipeline process"; } else if (tape == NULL && (tape = getenv("TAPE")) == NULL) tape = _PATH_DEFTAPE; if (strcmp(tape, "-") == 0) { pipeout++; tape = "standard output"; } if (blocksperfile) blocksperfile = rounddown(blocksperfile, ntrec); else if (!unlimited) { /* * Determine how to default tape size and density * * density tape size * 9-track 1600 bpi (160 bytes/.1") 2300 ft. * 9-track 6250 bpi (625 bytes/.1") 2300 ft. * cartridge 8000 bpi (100 bytes/.1") 1700 ft. * (450*4 - slop) * hilit19 hits again: " */ if (density == 0) density = cartridge ? 100 : 160; if (tsize == 0) tsize = cartridge ? 1700L*120L : 2300L*120L; } if (strchr(tape, ':')) { host = tape; tape = strchr(host, ':'); *tape++ = '\0'; #ifdef RDUMP if (strchr(tape, '\n')) { (void)fprintf(stderr, "invalid characters in tape\n"); exit(X_STARTUP); } if (rmthost(host) == 0) exit(X_STARTUP); #else (void)fprintf(stderr, "remote dump not enabled\n"); exit(X_STARTUP); #endif } (void)setuid(getuid()); /* rmthost() is the only reason to be setuid */ if (signal(SIGHUP, SIG_IGN) != SIG_IGN) signal(SIGHUP, sig); if (signal(SIGTRAP, SIG_IGN) != SIG_IGN) signal(SIGTRAP, sig); if (signal(SIGFPE, SIG_IGN) != SIG_IGN) signal(SIGFPE, sig); if (signal(SIGBUS, SIG_IGN) != SIG_IGN) signal(SIGBUS, sig); if (signal(SIGSEGV, SIG_IGN) != SIG_IGN) signal(SIGSEGV, sig); if (signal(SIGTERM, SIG_IGN) != SIG_IGN) signal(SIGTERM, sig); if (signal(SIGINT, interrupt) == SIG_IGN) signal(SIGINT, SIG_IGN); dump_getfstab(); /* /etc/fstab snarfed */ /* * disk can be either the full special file name, * the suffix of the special file name, * the special name missing the leading '/', * the file system name with or without the leading '/'. */ dt = fstabsearch(disk); if (dt != NULL) { disk = rawname(dt->fs_spec); if (disk == NULL) errx(X_STARTUP, "%s: unknown file system", dt->fs_spec); (void)strncpy(spcl.c_dev, dt->fs_spec, NAMELEN); (void)strncpy(spcl.c_filesys, dt->fs_file, NAMELEN); } else { (void)strncpy(spcl.c_dev, disk, NAMELEN); (void)strncpy(spcl.c_filesys, "an unlisted file system", NAMELEN); } spcl.c_dev[NAMELEN-1]='\0'; spcl.c_filesys[NAMELEN-1]='\0'; if ((mntpt = getmntpt(disk, &mntflags)) != NULL) { if (mntflags & MNT_RDONLY) { if (snapdump != 0) { msg("WARNING: %s\n", "-L ignored for read-only filesystem."); snapdump = 0; } } else if (snapdump == 0) { msg("WARNING: %s\n", "should use -L when dumping live read-write " "filesystems!"); } else { char snapname[BUFSIZ], snapcmd[BUFSIZ]; snprintf(snapname, sizeof snapname, "%s/.snap", mntpt); if ((stat(snapname, &sb) < 0) || !S_ISDIR(sb.st_mode)) { msg("WARNING: %s %s\n", "-L requested but snapshot location", snapname); msg(" %s: %s\n", "is not a directory", "dump downgraded, -L ignored"); snapdump = 0; } else { snprintf(snapname, sizeof snapname, "%s/.snap/dump_snapshot", mntpt); snprintf(snapcmd, sizeof snapcmd, "%s %s %s", _PATH_MKSNAP_FFS, mntpt, snapname); unlink(snapname); if (system(snapcmd) != 0) errx(X_STARTUP, "Cannot create %s: %s\n", snapname, strerror(errno)); if ((diskfd = open(snapname, O_RDONLY)) < 0) { unlink(snapname); errx(X_STARTUP, "Cannot open %s: %s\n", snapname, strerror(errno)); } unlink(snapname); if (fstat(diskfd, &sb) != 0) err(X_STARTUP, "%s: stat", snapname); spcl.c_date = _time_to_time64(sb.st_mtime); } } } else if (snapdump != 0) { msg("WARNING: Cannot use -L on an unmounted filesystem.\n"); snapdump = 0; } if (snapdump == 0) { if ((diskfd = open(disk, O_RDONLY)) < 0) err(X_STARTUP, "Cannot open %s", disk); if (fstat(diskfd, &sb) != 0) err(X_STARTUP, "%s: stat", disk); if (S_ISDIR(sb.st_mode)) errx(X_STARTUP, "%s: unknown file system", disk); } (void)strcpy(spcl.c_label, "none"); (void)gethostname(spcl.c_host, NAMELEN); spcl.c_level = level; spcl.c_type = TS_TAPE; if (rsync_friendly) { /* don't store real dump times */ spcl.c_date = 0; spcl.c_ddate = 0; } if (spcl.c_date == 0) { tmsg = "the epoch\n"; } else { time_t t = _time64_to_time(spcl.c_date); tmsg = ctime(&t); } msg("Date of this level %d dump: %s", level, tmsg); if (!Tflag && (!rsync_friendly)) getdumptime(); /* /etc/dumpdates snarfed */ if (spcl.c_ddate == 0) { tmsg = "the epoch\n"; } else { time_t t = _time64_to_time(spcl.c_ddate); tmsg = ctime(&t); } if (lastlevel < 0) msg("Date of last (level unknown) dump: %s", tmsg); else msg("Date of last level %d dump: %s", lastlevel, tmsg); msg("Dumping %s%s ", snapdump ? "snapshot of ": "", disk); if (dt != NULL) msgtail("(%s) ", dt->fs_file); if (host) msgtail("to %s on host %s\n", tape, host); else msgtail("to %s\n", tape); sync(); if ((ret = sbget(diskfd, &sblock, UFS_STDSB, UFS_NOCSUM)) != 0) { switch (ret) { case ENOENT: warn("Cannot find file system superblock"); return (1); default: warn("Unable to read file system superblock"); return (1); } } dev_bsize = sblock->fs_fsize / fsbtodb(sblock, 1); dev_bshift = ffs(dev_bsize) - 1; if (dev_bsize != (1 << dev_bshift)) quit("dev_bsize (%ld) is not a power of 2", dev_bsize); tp_bshift = ffs(TP_BSIZE) - 1; if (TP_BSIZE != (1 << tp_bshift)) quit("TP_BSIZE (%d) is not a power of 2", TP_BSIZE); maxino = sblock->fs_ipg * sblock->fs_ncg; mapsize = roundup(howmany(maxino, CHAR_BIT), TP_BSIZE); usedinomap = (char *)calloc((unsigned) mapsize, sizeof(char)); dumpdirmap = (char *)calloc((unsigned) mapsize, sizeof(char)); dumpinomap = (char *)calloc((unsigned) mapsize, sizeof(char)); tapesize = 3 * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); nonodump = spcl.c_level < honorlevel; passno = 1; setproctitle("%s: pass 1: regular files", disk); msg("mapping (Pass I) [regular files]\n"); anydirskipped = mapfiles(maxino, &tapesize); passno = 2; setproctitle("%s: pass 2: directories", disk); msg("mapping (Pass II) [directories]\n"); while (anydirskipped) { anydirskipped = mapdirs(maxino, &tapesize); } if (pipeout || unlimited) { tapesize += 10; /* 10 trailer blocks */ msg("estimated %ld tape blocks.\n", tapesize); } else { double fetapes; if (blocksperfile) fetapes = (double) tapesize / blocksperfile; else if (cartridge) { /* Estimate number of tapes, assuming streaming stops at the end of each block written, and not in mid-block. Assume no erroneous blocks; this can be compensated for with an artificially low tape size. */ fetapes = ( (double) tapesize /* blocks */ * TP_BSIZE /* bytes/block */ * (1.0/density) /* 0.1" / byte " */ + (double) tapesize /* blocks */ * (1.0/ntrec) /* streaming-stops per block */ * 15.48 /* 0.1" / streaming-stop " */ ) * (1.0 / tsize ); /* tape / 0.1" " */ } else { /* Estimate number of tapes, for old fashioned 9-track tape */ int tenthsperirg = (density == 625) ? 3 : 7; fetapes = ( (double) tapesize /* blocks */ * TP_BSIZE /* bytes / block */ * (1.0/density) /* 0.1" / byte " */ + (double) tapesize /* blocks */ * (1.0/ntrec) /* IRG's / block */ * tenthsperirg /* 0.1" / IRG " */ ) * (1.0 / tsize ); /* tape / 0.1" " */ } etapes = fetapes; /* truncating assignment */ etapes++; /* count the dumped inodes map on each additional tape */ tapesize += (etapes - 1) * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); tapesize += etapes + 10; /* headers + 10 trailer blks */ msg("estimated %ld tape blocks on %3.2f tape(s).\n", tapesize, fetapes); } /* * If the user only wants an estimate of the number of * tapes, exit now. */ if (just_estimate) exit(0); /* * Allocate tape buffer. */ if (!alloctape()) quit( "can't allocate tape buffers - try a smaller blocking factor.\n"); startnewtape(1); (void)time((time_t *)&(tstart_writing)); dumpmap(usedinomap, TS_CLRI, maxino - 1); passno = 3; setproctitle("%s: pass 3: directories", disk); msg("dumping (Pass III) [directories]\n"); dirty = 0; /* XXX just to get gcc to shut up */ for (map = dumpdirmap, ino = 1; ino < maxino; ino++) { if (((ino - 1) % CHAR_BIT) == 0) /* map is offset by 1 */ dirty = *map++; else dirty >>= 1; if ((dirty & 1) == 0) continue; /* * Skip directory inodes deleted and maybe reallocated */ dp = getino(ino, &mode); if (mode != IFDIR) continue; (void)dumpino(dp, ino); } passno = 4; setproctitle("%s: pass 4: regular files", disk); msg("dumping (Pass IV) [regular files]\n"); for (map = dumpinomap, ino = 1; ino < maxino; ino++) { if (((ino - 1) % CHAR_BIT) == 0) /* map is offset by 1 */ dirty = *map++; else dirty >>= 1; if ((dirty & 1) == 0) continue; /* * Skip inodes deleted and reallocated as directories. */ dp = getino(ino, &mode); if (mode == IFDIR) continue; (void)dumpino(dp, ino); } (void)time((time_t *)&(tend_writing)); spcl.c_type = TS_END; for (i = 0; i < ntrec; i++) writeheader(maxino - 1); if (pipeout) msg("DUMP: %jd tape blocks\n", (intmax_t)spcl.c_tapea); else msg("DUMP: %jd tape blocks on %d volume%s\n", (intmax_t)spcl.c_tapea, spcl.c_volume, (spcl.c_volume == 1) ? "" : "s"); /* report dump performance, avoid division through zero */ if (tend_writing - tstart_writing == 0) msg("finished in less than a second\n"); else msg("finished in %jd seconds, throughput %jd KBytes/sec\n", (intmax_t)tend_writing - tstart_writing, (intmax_t)(spcl.c_tapea / (tend_writing - tstart_writing))); putdumptime(); trewind(); broadcast("DUMP IS DONE!\a\a\n"); msg("DUMP IS DONE\n"); Exit(X_FINOK); /* NOTREACHED */ } static void usage(void) { fprintf(stderr, "usage: dump [-0123456789acLnSu] [-B records] [-b blocksize] [-C cachesize]\n" " [-D dumpdates] [-d density] [-f file | -P pipecommand] [-h level]\n" " [-s feet] [-T date] filesystem\n" " dump -W | -w\n"); exit(X_STARTUP); } /* * Check to see if a disk is currently mounted. */ static char * getmntpt(char *name, int *mntflagsp) { long mntsize, i; struct statfs *mntbuf; mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); for (i = 0; i < mntsize; i++) { if (!strcmp(mntbuf[i].f_mntfromname, name)) { *mntflagsp = mntbuf[i].f_flags; return (mntbuf[i].f_mntonname); } } return (0); } /* * Pick up a numeric argument. It must be nonnegative and in the given * range (except that a vmax of 0 means unlimited). */ static long numarg(const char *meaning, long vmin, long vmax) { char *p; long val; val = strtol(optarg, &p, 10); if (*p) errx(1, "illegal %s -- %s", meaning, optarg); if (val < vmin || (vmax && val > vmax)) errx(1, "%s must be between %ld and %ld", meaning, vmin, vmax); return (val); } void sig(int signo) { switch(signo) { case SIGALRM: case SIGBUS: case SIGFPE: case SIGHUP: case SIGTERM: case SIGTRAP: if (pipeout) quit("Signal on pipe: cannot recover\n"); msg("Rewriting attempted as response to unknown signal.\n"); (void)fflush(stderr); (void)fflush(stdout); close_rewind(); exit(X_REWRITE); /* NOTREACHED */ case SIGSEGV: msg("SIGSEGV: ABORTING!\n"); (void)signal(SIGSEGV, SIG_DFL); (void)kill(0, SIGSEGV); /* NOTREACHED */ } } char * rawname(char *cp) { struct stat sb; /* * Ensure that the device passed in is a raw device. */ if (stat(cp, &sb) == 0 && (sb.st_mode & S_IFMT) == S_IFCHR) return (cp); /* * Since there's only one device type now, we can't construct any * better name, so we have to return NULL. */ return (NULL); } /* * obsolete -- * Change set of key letters and ordered arguments into something * getopt(3) will like. */ static void obsolete(int *argcp, char **argvp[]) { int argc, flags; char *ap, **argv, *flagsp, **nargv, *p; /* Setup. */ argv = *argvp; argc = *argcp; /* * Return if no arguments or first argument has leading * dash or slash. */ ap = argv[1]; if (argc == 1 || *ap == '-' || *ap == '/') return; /* Allocate space for new arguments. */ if ((*argvp = nargv = malloc((argc + 1) * sizeof(char *))) == NULL || (p = flagsp = malloc(strlen(ap) + 2)) == NULL) err(1, NULL); *nargv++ = *argv; argv += 2; for (flags = 0; *ap; ++ap) { switch (*ap) { case 'B': case 'b': case 'd': case 'f': case 'D': case 'C': case 'h': case 's': case 'T': if (*argv == NULL) { warnx("option requires an argument -- %c", *ap); usage(); } if ((nargv[0] = malloc(strlen(*argv) + 2 + 1)) == NULL) err(1, NULL); nargv[0][0] = '-'; nargv[0][1] = *ap; (void)strcpy(&nargv[0][2], *argv); ++argv; ++nargv; break; default: if (!flags) { *p++ = '-'; flags = 1; } *p++ = *ap; break; } } /* Terminate flags. */ if (flags) { *p = '\0'; *nargv++ = flagsp; } else free(flagsp); /* Copy remaining arguments. */ while ((*nargv++ = *argv++)); /* Update argument count. */ *argcp = nargv - *argvp - 1; } diff --git a/sbin/dump/optr.c b/sbin/dump/optr.c index 8eb163516c72..d3ca5b2a926e 100644 --- a/sbin/dump/optr.c +++ b/sbin/dump/optr.c @@ -1,431 +1,429 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)optr.c 8.2 (Berkeley) 1/6/94"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dump.h" #include "pathnames.h" void alarmcatch(int); int datesort(const void *, const void *); /* * Query the operator; This previously-fascist piece of code * no longer requires an exact response. * It is intended to protect dump aborting by inquisitive * people banging on the console terminal to see what is * happening which might cause dump to croak, destroying * a large number of hours of work. * * Every 2 minutes we reprint the message, alerting others * that dump needs attention. */ static int timeout; static const char *attnmessage; /* attention message */ int query(const char *question) { char replybuffer[64]; int back, errcount; FILE *mytty; if ((mytty = fopen(_PATH_TTY, "r")) == NULL) quit("fopen on %s fails: %s\n", _PATH_TTY, strerror(errno)); attnmessage = question; timeout = 0; alarmcatch(0); back = -1; errcount = 0; do { if (fgets(replybuffer, 63, mytty) == NULL) { clearerr(mytty); if (++errcount > 30) /* XXX ugly */ quit("excessive operator query failures\n"); } else if (replybuffer[0] == 'y' || replybuffer[0] == 'Y') { back = 1; } else if (replybuffer[0] == 'n' || replybuffer[0] == 'N') { back = 0; } else { (void) fprintf(stderr, " DUMP: \"Yes\" or \"No\"?\n"); (void) fprintf(stderr, " DUMP: %s: (\"yes\" or \"no\") ", question); } } while (back < 0); /* * Turn off the alarm, and reset the signal to trap out.. */ (void) alarm(0); if (signal(SIGALRM, sig) == SIG_IGN) signal(SIGALRM, SIG_IGN); (void) fclose(mytty); return(back); } char lastmsg[BUFSIZ]; /* * Alert the console operator, and enable the alarm clock to * sleep for 2 minutes in case nobody comes to satisfy dump */ void alarmcatch(int sig __unused) { if (notify == 0) { if (timeout == 0) (void) fprintf(stderr, " DUMP: %s: (\"yes\" or \"no\") ", attnmessage); else msgtail("\a\a"); } else { if (timeout) { msgtail("\n"); broadcast(""); /* just print last msg */ } (void) fprintf(stderr," DUMP: %s: (\"yes\" or \"no\") ", attnmessage); } signal(SIGALRM, alarmcatch); (void) alarm(120); timeout = 1; } /* * Here if an inquisitive operator interrupts the dump program */ void interrupt(int signo __unused) { msg("Interrupt received.\n"); if (query("Do you want to abort dump?")) dumpabort(0); } /* * We now use wall(1) to do the actual broadcasting. */ void broadcast(const char *message) { FILE *fp; char buf[sizeof(_PATH_WALL) + sizeof(OPGRENT) + 3]; if (!notify) return; snprintf(buf, sizeof(buf), "%s -g %s", _PATH_WALL, OPGRENT); if ((fp = popen(buf, "w")) == NULL) return; (void) fputs("\a\a\aMessage from the dump program to all operators\n\nDUMP: NEEDS ATTENTION: ", fp); if (lastmsg[0]) (void) fputs(lastmsg, fp); if (message[0]) (void) fputs(message, fp); (void) pclose(fp); } /* * Print out an estimate of the amount of time left to do the dump */ time_t tschedule = 0; void timeest(void) { double percent; time_t tnow, tdone; char *tdone_str; int deltat, hours, mins; (void)time(&tnow); if (blockswritten > tapesize) { setproctitle("%s: 99.99%% done, finished soon", disk); if (tnow >= tschedule) { tschedule = tnow + 300; msg("99.99%% done, finished soon\n"); } } else { deltat = (blockswritten == 0) ? 0 : tstart_writing - tnow + (double)(tnow - tstart_writing) / blockswritten * tapesize; tdone = tnow + deltat; percent = (blockswritten * 100.0) / tapesize; hours = deltat / 3600; mins = (deltat % 3600) / 60; tdone_str = ctime(&tdone); tdone_str[strlen(tdone_str) - 1] = '\0'; setproctitle( "%s: pass %d: %3.2f%% done, finished in %d:%02d at %s", disk, passno, percent, hours, mins, tdone_str); if (tnow >= tschedule) { tschedule = tnow + 300; if (blockswritten < 500) return; msg("%3.2f%% done, finished in %d:%02d at %s\n", percent, hours, mins, tdone_str); } } } /* * Schedule a printout of the estimate in the next call to timeest(). */ void infosch(int signal __unused) { tschedule = 0; } void msg(const char *fmt, ...) { va_list ap; (void) fprintf(stderr," DUMP: "); #ifdef TDEBUG (void) fprintf(stderr, "pid=%d ", getpid()); #endif va_start(ap, fmt); (void) vfprintf(stderr, fmt, ap); va_end(ap); (void) fflush(stdout); (void) fflush(stderr); va_start(ap, fmt); (void) vsnprintf(lastmsg, sizeof(lastmsg), fmt, ap); va_end(ap); } void msgtail(const char *fmt, ...) { va_list ap; va_start(ap, fmt); (void) vfprintf(stderr, fmt, ap); va_end(ap); } void quit(const char *fmt, ...) { va_list ap; (void) fprintf(stderr," DUMP: "); #ifdef TDEBUG (void) fprintf(stderr, "pid=%d ", getpid()); #endif va_start(ap, fmt); (void) vfprintf(stderr, fmt, ap); va_end(ap); (void) fflush(stdout); (void) fflush(stderr); dumpabort(0); } /* * Tell the operator what has to be done; * we don't actually do it */ struct fstab * allocfsent(const struct fstab *fs) { struct fstab *new; new = (struct fstab *)malloc(sizeof (*fs)); if (new == NULL || (new->fs_file = strdup(fs->fs_file)) == NULL || (new->fs_type = strdup(fs->fs_type)) == NULL || (new->fs_spec = strdup(fs->fs_spec)) == NULL) quit("%s\n", strerror(errno)); new->fs_passno = fs->fs_passno; new->fs_freq = fs->fs_freq; return (new); } struct pfstab { SLIST_ENTRY(pfstab) pf_list; struct fstab *pf_fstab; }; static SLIST_HEAD(, pfstab) table; void dump_getfstab(void) { struct fstab *fs; struct pfstab *pf; if (setfsent() == 0) { msg("Can't open %s for dump table information: %s\n", _PATH_FSTAB, strerror(errno)); return; } while ((fs = getfsent()) != NULL) { if ((strcmp(fs->fs_type, FSTAB_RW) && strcmp(fs->fs_type, FSTAB_RO) && strcmp(fs->fs_type, FSTAB_RQ)) || strcmp(fs->fs_vfstype, "ufs")) continue; fs = allocfsent(fs); if ((pf = (struct pfstab *)malloc(sizeof (*pf))) == NULL) quit("%s\n", strerror(errno)); pf->pf_fstab = fs; SLIST_INSERT_HEAD(&table, pf, pf_list); } (void) endfsent(); } /* * Search in the fstab for a file name. * This file name can be either the special or the path file name. * * The file name can omit the leading '/'. */ struct fstab * fstabsearch(const char *key) { struct pfstab *pf; struct fstab *fs; char *rn; SLIST_FOREACH(pf, &table, pf_list) { fs = pf->pf_fstab; if (strcmp(fs->fs_file, key) == 0 || strcmp(fs->fs_spec, key) == 0) return (fs); rn = rawname(fs->fs_spec); if (rn != NULL && strcmp(rn, key) == 0) return (fs); if (key[0] != '/') { if (*fs->fs_spec == '/' && strcmp(fs->fs_spec + 1, key) == 0) return (fs); if (*fs->fs_file == '/' && strcmp(fs->fs_file + 1, key) == 0) return (fs); } } return (NULL); } /* * Tell the operator what to do */ void lastdump(int arg) /* w ==> just what to do; W ==> most recent dumps */ { int i; struct fstab *dt; struct dumpdates *dtwalk; char *lastname, *date; int dumpme; time_t tnow; struct tm *tlast; (void) time(&tnow); dump_getfstab(); /* /etc/fstab input */ initdumptimes(); /* /etc/dumpdates input */ qsort((char *) ddatev, nddates, sizeof(struct dumpdates *), datesort); if (arg == 'w') (void) printf("Dump these file systems:\n"); else (void) printf("Last dump(s) done (Dump '>' file systems):\n"); lastname = "??"; ITITERATE(i, dtwalk) { if (strncmp(lastname, dtwalk->dd_name, sizeof(dtwalk->dd_name)) == 0) continue; date = (char *)ctime(&dtwalk->dd_ddate); date[16] = '\0'; /* blast away seconds and year */ lastname = dtwalk->dd_name; dt = fstabsearch(dtwalk->dd_name); dumpme = (dt != NULL && dt->fs_freq != 0); if (dumpme) { tlast = localtime(&dtwalk->dd_ddate); dumpme = tnow > (dtwalk->dd_ddate - (tlast->tm_hour * 3600) - (tlast->tm_min * 60) - tlast->tm_sec + (dt->fs_freq * 86400)); } if (arg != 'w' || dumpme) (void) printf( "%c %8s\t(%6s) Last dump: Level %d, Date %s\n", dumpme && (arg != 'w') ? '>' : ' ', dtwalk->dd_name, dt ? dt->fs_file : "", dtwalk->dd_level, date); } } int datesort(const void *a1, const void *a2) { struct dumpdates *d1 = *(struct dumpdates **)a1; struct dumpdates *d2 = *(struct dumpdates **)a2; int diff; diff = strncmp(d1->dd_name, d2->dd_name, sizeof(d1->dd_name)); if (diff == 0) return (d2->dd_ddate - d1->dd_ddate); return (diff); } diff --git a/sbin/dump/tape.c b/sbin/dump/tape.c index c123f7fa9404..3a3574f6d44d 100644 --- a/sbin/dump/tape.c +++ b/sbin/dump/tape.c @@ -1,901 +1,899 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)tape.c 8.4 (Berkeley) 5/1/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dump.h" ino_t curino; /* current inumber; used globally */ int newtape; /* new tape flag */ union u_spcl u_spcl; /* mapping of variables in a control block */ static int tapefd; /* tape file descriptor */ static long asize; /* number of 0.1" units written on cur tape */ static int writesize; /* size of malloc()ed buffer for tape */ static int64_t lastspclrec = -1; /* tape block number of last written header */ static int trecno = 0; /* next record to write in current block */ static long blocksthisvol; /* number of blocks on current output file */ static char *nexttape; static FILE *popenfp = NULL; static int atomic_read(int, void *, int); static int atomic_write(int, const void *, int); static void worker(int, int); static void create_workers(void); static void flushtape(void); static void killall(void); static void rollforward(void); /* * Concurrent dump mods (Caltech) - disk block reading and tape writing * are exported to several worker processes. While one worker writes the * tape, the others read disk blocks; they pass control of the tape in * a ring via signals. The parent process traverses the file system and * sends writeheader()'s and lists of daddr's to the workers via pipes. * The following structure defines the instruction packets sent to workers. */ struct req { ufs2_daddr_t dblk; int count; }; static int reqsiz; #define WORKERS 3 /* 1 worker writing, 1 reading, 1 for slack */ static struct worker { int64_t tapea; /* header number at start of this chunk */ int64_t firstrec; /* record number of this block */ int count; /* count to next header (used for TS_TAPE */ /* after EOT) */ int inode; /* inode that we are currently dealing with */ int fd; /* FD for this worker */ int pid; /* PID for this worker */ int sent; /* 1 == we've sent this worker requests */ char (*tblock)[TP_BSIZE]; /* buffer for data blocks */ struct req *req; /* buffer for requests */ } workers[WORKERS+1]; static struct worker *wp; static char (*nextblock)[TP_BSIZE]; static int master; /* pid of master, for sending error signals */ static int tenths; /* length of tape used per block written */ static volatile sig_atomic_t caught; /* have we caught the signal to proceed? */ static volatile sig_atomic_t ready; /* reached the lock point without having */ /* received the SIGUSR2 signal from the prev worker? */ static jmp_buf jmpbuf; /* where to jump to if we are ready when the */ /* SIGUSR2 arrives from the previous worker */ int alloctape(void) { int pgoff = getpagesize() - 1; char *buf; int i; writesize = ntrec * TP_BSIZE; reqsiz = (ntrec + 1) * sizeof(struct req); /* * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require * repositioning after stopping, i.e, streaming mode, where the gap is * variable, 0.30" to 0.45". The gap is maximal when the tape stops. */ if (blocksperfile == 0 && !unlimited) tenths = writesize / density + (cartridge ? 16 : density == 625 ? 5 : 8); /* * Allocate tape buffer contiguous with the array of instruction * packets, so flushtape() can write them together with one write(). * Align tape buffer on page boundary to speed up tape write(). */ for (i = 0; i <= WORKERS; i++) { buf = (char *) malloc((unsigned)(reqsiz + writesize + pgoff + TP_BSIZE)); if (buf == NULL) return(0); workers[i].tblock = (char (*)[TP_BSIZE]) (((long)&buf[ntrec + 1] + pgoff) &~ pgoff); workers[i].req = (struct req *)workers[i].tblock - ntrec - 1; } wp = &workers[0]; wp->count = 1; wp->tapea = 0; wp->firstrec = 0; nextblock = wp->tblock; return(1); } void writerec(char *dp, int isspcl) { wp->req[trecno].dblk = (ufs2_daddr_t)0; wp->req[trecno].count = 1; /* Can't do a structure assignment due to alignment problems */ bcopy(dp, *(nextblock)++, sizeof (union u_spcl)); if (isspcl) lastspclrec = spcl.c_tapea; trecno++; spcl.c_tapea++; if (trecno >= ntrec) flushtape(); } void dumpblock(ufs2_daddr_t blkno, int size) { int avail, tpblks; ufs2_daddr_t dblkno; dblkno = fsbtodb(sblock, blkno); tpblks = size >> tp_bshift; while ((avail = MIN(tpblks, ntrec - trecno)) > 0) { wp->req[trecno].dblk = dblkno; wp->req[trecno].count = avail; trecno += avail; spcl.c_tapea += avail; if (trecno >= ntrec) flushtape(); dblkno += avail << (tp_bshift - dev_bshift); tpblks -= avail; } } int nogripe = 0; void tperror(int signo __unused) { if (pipeout) { msg("write error on %s\n", tape); quit("Cannot recover\n"); /* NOTREACHED */ } msg("write error %ld blocks into volume %d\n", blocksthisvol, tapeno); broadcast("DUMP WRITE ERROR!\n"); if (!query("Do you want to restart?")) dumpabort(0); msg("Closing this volume. Prepare to restart with new media;\n"); msg("this dump volume will be rewritten.\n"); killall(); nogripe = 1; close_rewind(); Exit(X_REWRITE); } void sigpipe(int signo __unused) { quit("Broken pipe\n"); } static void flushtape(void) { int i, blks, got; int64_t lastfirstrec; int siz = (char *)nextblock - (char *)wp->req; wp->req[trecno].count = 0; /* Sentinel */ if (atomic_write(wp->fd, (const void *)wp->req, siz) != siz) quit("error writing command pipe: %s\n", strerror(errno)); wp->sent = 1; /* we sent a request, read the response later */ lastfirstrec = wp->firstrec; if (++wp >= &workers[WORKERS]) wp = &workers[0]; /* Read results back from next worker */ if (wp->sent) { if (atomic_read(wp->fd, (void *)&got, sizeof got) != sizeof got) { perror(" DUMP: error reading command pipe in master"); dumpabort(0); } wp->sent = 0; /* Check for end of tape */ if (got < writesize) { msg("End of tape detected\n"); /* * Drain the results, don't care what the values were. * If we read them here then trewind won't... */ for (i = 0; i < WORKERS; i++) { if (workers[i].sent) { if (atomic_read(workers[i].fd, (void *)&got, sizeof got) != sizeof got) { perror(" DUMP: error reading command pipe in master"); dumpabort(0); } workers[i].sent = 0; } } close_rewind(); rollforward(); return; } } blks = 0; if (spcl.c_type != TS_END && spcl.c_type != TS_CLRI && spcl.c_type != TS_BITS) { assert(spcl.c_count <= TP_NINDIR); for (i = 0; i < spcl.c_count; i++) if (spcl.c_addr[i] != 0) blks++; } wp->count = lastspclrec + blks + 1 - spcl.c_tapea; wp->tapea = spcl.c_tapea; wp->firstrec = lastfirstrec + ntrec; wp->inode = curino; nextblock = wp->tblock; trecno = 0; asize += tenths; blockswritten += ntrec; blocksthisvol += ntrec; if (!pipeout && !unlimited && (blocksperfile ? (blocksthisvol >= blocksperfile) : (asize > tsize))) { close_rewind(); startnewtape(0); } timeest(); } void trewind(void) { struct stat sb; int f; int got; for (f = 0; f < WORKERS; f++) { /* * Drain the results, but unlike EOT we DO (or should) care * what the return values were, since if we detect EOT after * we think we've written the last blocks to the tape anyway, * we have to replay those blocks with rollforward. * * fixme: punt for now. */ if (workers[f].sent) { if (atomic_read(workers[f].fd, (void *)&got, sizeof got) != sizeof got) { perror(" DUMP: error reading command pipe in master"); dumpabort(0); } workers[f].sent = 0; if (got != writesize) { msg("EOT detected in last 2 tape records!\n"); msg("Use a longer tape, decrease the size estimate\n"); quit("or use no size estimate at all.\n"); } } (void) close(workers[f].fd); } while (wait((int *)NULL) >= 0) /* wait for any signals from workers */ /* void */; if (pipeout) return; msg("Closing %s\n", tape); if (popenout) { tapefd = -1; (void)pclose(popenfp); popenfp = NULL; return; } #ifdef RDUMP if (host) { rmtclose(); while (rmtopen(tape, 0) < 0) sleep(10); rmtclose(); return; } #endif if (fstat(tapefd, &sb) == 0 && S_ISFIFO(sb.st_mode)) { (void)close(tapefd); return; } (void) close(tapefd); while ((f = open(tape, 0)) < 0) sleep (10); (void) close(f); } void close_rewind() { time_t tstart_changevol, tend_changevol; trewind(); if (nexttape) return; (void)time((time_t *)&(tstart_changevol)); if (!nogripe) { msg("Change Volumes: Mount volume #%d\n", tapeno+1); broadcast("CHANGE DUMP VOLUMES!\a\a\n"); } while (!query("Is the new volume mounted and ready to go?")) if (query("Do you want to abort?")) { dumpabort(0); /*NOTREACHED*/ } (void)time((time_t *)&(tend_changevol)); if ((tstart_changevol != (time_t)-1) && (tend_changevol != (time_t)-1)) tstart_writing += (tend_changevol - tstart_changevol); } void rollforward(void) { struct req *p, *q, *prev; struct worker *twp; int i, size, got; int64_t savedtapea; union u_spcl *ntb, *otb; twp = &workers[WORKERS]; ntb = (union u_spcl *)twp->tblock[1]; /* * Each of the N workers should have requests that need to * be replayed on the next tape. Use the extra worker buffers * (workers[WORKERS]) to construct request lists to be sent to * each worker in turn. */ for (i = 0; i < WORKERS; i++) { q = &twp->req[1]; otb = (union u_spcl *)wp->tblock; /* * For each request in the current worker, copy it to twp. */ prev = NULL; for (p = wp->req; p->count > 0; p += p->count) { *q = *p; if (p->dblk == 0) *ntb++ = *otb++; /* copy the datablock also */ prev = q; q += q->count; } if (prev == NULL) quit("rollforward: protocol botch"); if (prev->dblk != 0) prev->count -= 1; else ntb--; q -= 1; q->count = 0; q = &twp->req[0]; if (i == 0) { q->dblk = 0; q->count = 1; trecno = 0; nextblock = twp->tblock; savedtapea = spcl.c_tapea; spcl.c_tapea = wp->tapea; startnewtape(0); spcl.c_tapea = savedtapea; lastspclrec = savedtapea - 1; } size = (char *)ntb - (char *)q; if (atomic_write(wp->fd, (const void *)q, size) != size) { perror(" DUMP: error writing command pipe"); dumpabort(0); } wp->sent = 1; if (++wp >= &workers[WORKERS]) wp = &workers[0]; q->count = 1; if (prev->dblk != 0) { /* * If the last one was a disk block, make the * first of this one be the last bit of that disk * block... */ q->dblk = prev->dblk + prev->count * (TP_BSIZE / DEV_BSIZE); ntb = (union u_spcl *)twp->tblock; } else { /* * It wasn't a disk block. Copy the data to its * new location in the buffer. */ q->dblk = 0; *((union u_spcl *)twp->tblock) = *ntb; ntb = (union u_spcl *)twp->tblock[1]; } } wp->req[0] = *q; nextblock = wp->tblock; if (q->dblk == 0) nextblock++; trecno = 1; /* * Clear the first workers' response. One hopes that it * worked ok, otherwise the tape is much too short! */ if (wp->sent) { if (atomic_read(wp->fd, (void *)&got, sizeof got) != sizeof got) { perror(" DUMP: error reading command pipe in master"); dumpabort(0); } wp->sent = 0; if (got != writesize) { quit("EOT detected at start of the tape!\n"); } } } /* * We implement taking and restoring checkpoints on the tape level. * When each tape is opened, a new process is created by forking; this * saves all of the necessary context in the parent. The child * continues the dump; the parent waits around, saving the context. * If the child returns X_REWRITE, then it had problems writing that tape; * this causes the parent to fork again, duplicating the context, and * everything continues as if nothing had happened. */ void startnewtape(int top) { int parentpid; int childpid; int status; char *p; sig_t interrupt_save; interrupt_save = signal(SIGINT, SIG_IGN); parentpid = getpid(); restore_check_point: (void)signal(SIGINT, interrupt_save); /* * All signals are inherited... */ setproctitle(NULL); /* Restore the proctitle. */ childpid = fork(); if (childpid < 0) { msg("Context save fork fails in parent %d\n", parentpid); Exit(X_ABORT); } if (childpid != 0) { /* * PARENT: * save the context by waiting * until the child doing all of the work returns. * don't catch the interrupt */ signal(SIGINT, SIG_IGN); #ifdef TDEBUG msg("Tape: %d; parent process: %d child process %d\n", tapeno+1, parentpid, childpid); #endif /* TDEBUG */ if (waitpid(childpid, &status, 0) == -1) msg("Waiting for child %d: %s\n", childpid, strerror(errno)); if (status & 0xFF) { msg("Child %d returns LOB status %o\n", childpid, status&0xFF); } status = (status >> 8) & 0xFF; #ifdef TDEBUG switch(status) { case X_FINOK: msg("Child %d finishes X_FINOK\n", childpid); break; case X_ABORT: msg("Child %d finishes X_ABORT\n", childpid); break; case X_REWRITE: msg("Child %d finishes X_REWRITE\n", childpid); break; default: msg("Child %d finishes unknown %d\n", childpid, status); break; } #endif /* TDEBUG */ switch(status) { case X_FINOK: Exit(X_FINOK); case X_ABORT: Exit(X_ABORT); case X_REWRITE: goto restore_check_point; default: msg("Bad return code from dump: %d\n", status); Exit(X_ABORT); } /*NOTREACHED*/ } else { /* we are the child; just continue */ #ifdef TDEBUG sleep(4); /* allow time for parent's message to get out */ msg("Child on Tape %d has parent %d, my pid = %d\n", tapeno+1, parentpid, getpid()); #endif /* TDEBUG */ /* * If we have a name like "/dev/rmt0,/dev/rmt1", * use the name before the comma first, and save * the remaining names for subsequent volumes. */ tapeno++; /* current tape sequence */ if (nexttape || strchr(tape, ',')) { if (nexttape && *nexttape) tape = nexttape; if ((p = strchr(tape, ',')) != NULL) { *p = '\0'; nexttape = p + 1; } else nexttape = NULL; msg("Dumping volume %d on %s\n", tapeno, tape); } if (pipeout) { tapefd = STDOUT_FILENO; } else if (popenout) { char volno[sizeof("2147483647")]; (void)sprintf(volno, "%d", spcl.c_volume + 1); if (setenv("DUMP_VOLUME", volno, 1) == -1) { msg("Cannot set $DUMP_VOLUME.\n"); dumpabort(0); } popenfp = popen(popenout, "w"); if (popenfp == NULL) { msg("Cannot open output pipeline \"%s\".\n", popenout); dumpabort(0); } tapefd = fileno(popenfp); } else { #ifdef RDUMP while ((tapefd = (host ? rmtopen(tape, 2) : open(tape, O_WRONLY|O_CREAT, 0666))) < 0) #else while ((tapefd = open(tape, O_WRONLY|O_CREAT, 0666)) < 0) #endif { msg("Cannot open output \"%s\".\n", tape); if (!query("Do you want to retry the open?")) dumpabort(0); } } create_workers(); /* Share open tape file descriptor with workers */ if (popenout) close(tapefd); /* Give up our copy of it. */ signal(SIGINFO, infosch); asize = 0; blocksthisvol = 0; if (top) newtape++; /* new tape signal */ spcl.c_count = wp->count; /* * measure firstrec in TP_BSIZE units since restore doesn't * know the correct ntrec value... */ spcl.c_firstrec = wp->firstrec; spcl.c_volume++; spcl.c_type = TS_TAPE; writeheader((ino_t)wp->inode); if (tapeno > 1) msg("Volume %d begins with blocks from inode %d\n", tapeno, wp->inode); } } void dumpabort(int signo __unused) { if (master != 0 && master != getpid()) /* Signals master to call dumpabort */ (void) kill(master, SIGTERM); else { killall(); msg("The ENTIRE dump is aborted.\n"); } #ifdef RDUMP rmtclose(); #endif Exit(X_ABORT); } void Exit(int status) { #ifdef TDEBUG msg("pid = %d exits with status %d\n", getpid(), status); #endif /* TDEBUG */ exit(status); } /* * proceed - handler for SIGUSR2, used to synchronize IO between the workers. */ void proceed(int signo __unused) { if (ready) longjmp(jmpbuf, 1); caught++; } void create_workers(void) { int cmd[2]; int i, j; master = getpid(); signal(SIGTERM, dumpabort); /* Worker sends SIGTERM on dumpabort() */ signal(SIGPIPE, sigpipe); signal(SIGUSR1, tperror); /* Worker sends SIGUSR1 on tape errors */ signal(SIGUSR2, proceed); /* Worker sends SIGUSR2 to next worker */ for (i = 0; i < WORKERS; i++) { if (i == wp - &workers[0]) { caught = 1; } else { caught = 0; } if (socketpair(AF_UNIX, SOCK_STREAM, 0, cmd) < 0 || (workers[i].pid = fork()) < 0) quit("too many workers, %d (recompile smaller): %s\n", i, strerror(errno)); workers[i].fd = cmd[1]; workers[i].sent = 0; if (workers[i].pid == 0) { /* Worker starts up here */ for (j = 0; j <= i; j++) (void) close(workers[j].fd); signal(SIGINT, SIG_IGN); /* Master handles this */ worker(cmd[0], i); Exit(X_FINOK); } } for (i = 0; i < WORKERS; i++) (void) atomic_write(workers[i].fd, (const void *) &workers[(i + 1) % WORKERS].pid, sizeof workers[0].pid); master = 0; } void killall(void) { int i; for (i = 0; i < WORKERS; i++) if (workers[i].pid > 0) { (void) kill(workers[i].pid, SIGKILL); workers[i].sent = 0; } } /* * Synchronization - each process has a lockfile, and shares file * descriptors to the following process's lockfile. When our write * completes, we release our lock on the following process's lock- * file, allowing the following process to lock it and proceed. We * get the lock back for the next cycle by swapping descriptors. */ static void worker(int cmd, int worker_number) { int nread; int nextworker, size, wrote, eot_count; /* * Need our own seek pointer. */ (void) close(diskfd); if ((diskfd = open(disk, O_RDONLY)) < 0) quit("worker couldn't reopen disk: %s\n", strerror(errno)); /* * Need the pid of the next worker in the loop... */ if ((nread = atomic_read(cmd, (void *)&nextworker, sizeof nextworker)) != sizeof nextworker) { quit("master/worker protocol botched - didn't get pid of next worker.\n"); } /* * Get list of blocks to dump, read the blocks into tape buffer */ while ((nread = atomic_read(cmd, (void *)wp->req, reqsiz)) == reqsiz) { struct req *p = wp->req; for (trecno = 0; trecno < ntrec; trecno += p->count, p += p->count) { if (p->dblk) { blkread(p->dblk, wp->tblock[trecno], p->count * TP_BSIZE); } else { if (p->count != 1 || atomic_read(cmd, (void *)wp->tblock[trecno], TP_BSIZE) != TP_BSIZE) quit("master/worker protocol botched.\n"); } } if (setjmp(jmpbuf) == 0) { ready = 1; if (!caught) (void) pause(); } ready = 0; caught = 0; /* Try to write the data... */ eot_count = 0; size = 0; wrote = 0; while (eot_count < 10 && size < writesize) { #ifdef RDUMP if (host) wrote = rmtwrite(wp->tblock[0]+size, writesize-size); else #endif wrote = write(tapefd, wp->tblock[0]+size, writesize-size); #ifdef WRITEDEBUG printf("worker %d wrote %d\n", worker_number, wrote); #endif if (wrote < 0) break; if (wrote == 0) eot_count++; size += wrote; } #ifdef WRITEDEBUG if (size != writesize) printf("worker %d only wrote %d out of %d bytes and gave up.\n", worker_number, size, writesize); #endif /* * Handle ENOSPC as an EOT condition. */ if (wrote < 0 && errno == ENOSPC) { wrote = 0; eot_count++; } if (eot_count > 0) size = 0; if (wrote < 0) { (void) kill(master, SIGUSR1); for (;;) (void) sigpause(0); } else { /* * pass size of write back to master * (for EOT handling) */ (void)atomic_write(cmd, (const void *)&size, sizeof size); } /* * If partial write, don't want next worker to go. * Also jolts him awake. */ (void) kill(nextworker, SIGUSR2); } if (nread != 0) quit("error reading command pipe: %s\n", strerror(errno)); } /* * Since a read from a pipe may not return all we asked for, * loop until the count is satisfied (or error). */ static int atomic_read(int fd, void *buf, int count) { int got, need = count; while ((got = read(fd, buf, need)) > 0 && (need -= got) > 0) buf += got; return (got < 0 ? got : count - need); } /* * Since a write to a pipe may not write all we ask if we get a signal, * loop until the count is satisfied (or error). */ static int atomic_write(int fd, const void *buf, int count) { int got, need = count; while ((got = write(fd, buf, need)) > 0 && (need -= got) > 0) buf += got; return (got < 0 ? got : count - need); } diff --git a/sbin/dump/traverse.c b/sbin/dump/traverse.c index 08e902667759..281cffcdf6f2 100644 --- a/sbin/dump/traverse.c +++ b/sbin/dump/traverse.c @@ -1,1010 +1,1008 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1988, 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)traverse.c 8.7 (Berkeley) 6/15/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dump.h" union dinode { struct ufs1_dinode dp1; struct ufs2_dinode dp2; }; #define DIP(dp, field) \ ((sblock->fs_magic == FS_UFS1_MAGIC) ? \ (dp)->dp1.field : (dp)->dp2.field) #define DIP_SET(dp, field, val) do {\ if (sblock->fs_magic == FS_UFS1_MAGIC) \ (dp)->dp1.field = (val); \ else \ (dp)->dp2.field = (val); \ } while (0) #define HASDUMPEDFILE 0x1 #define HASSUBDIRS 0x2 static int dirindir(ino_t ino, ufs2_daddr_t blkno, int level, long *size, long *tapesize, int nodump, ino_t maxino); static void dmpindir(union dinode *dp, ino_t ino, ufs2_daddr_t blk, int level, off_t *size); static void ufs1_blksout(ufs1_daddr_t *blkp, int frags, ino_t ino); static void ufs2_blksout(union dinode *dp, ufs2_daddr_t *blkp, int frags, ino_t ino, int last); static int appendextdata(union dinode *dp); static void writeextdata(union dinode *dp, ino_t ino, int added); static int searchdir(ino_t ino, ufs2_daddr_t blkno, long size, long filesize, long *tapesize, int nodump, ino_t maxino); static long blockest(union dinode *dp); /* * This is an estimation of the number of TP_BSIZE blocks in the file. * It estimates the number of blocks in files with holes by assuming * that all of the blocks accounted for by di_blocks are data blocks * (when some of the blocks are usually used for indirect pointers); * hence the estimate may be high. */ static long blockest(union dinode *dp) { long blkest, sizeest; /* * dp->di_size is the size of the file in bytes. * dp->di_blocks stores the number of sectors actually in the file. * If there are more sectors than the size would indicate, this just * means that there are indirect blocks in the file or unused * sectors in the last file block; we can safely ignore these * (blkest = sizeest below). * If the file is bigger than the number of sectors would indicate, * then the file has holes in it. In this case we must use the * block count to estimate the number of data blocks used, but * we use the actual size for estimating the number of indirect * dump blocks (sizeest vs. blkest in the indirect block * calculation). */ if ((DIP(dp, di_flags) & SF_SNAPSHOT) != 0) return (1); blkest = howmany(dbtob(DIP(dp, di_blocks)), TP_BSIZE); sizeest = howmany(DIP(dp, di_size), TP_BSIZE); if (blkest > sizeest) blkest = sizeest; if (DIP(dp, di_size) > sblock->fs_bsize * UFS_NDADDR) { /* calculate the number of indirect blocks on the dump tape */ blkest += howmany(sizeest - UFS_NDADDR * sblock->fs_bsize / TP_BSIZE, TP_NINDIR); } return (blkest + 1); } /* Auxiliary macro to pick up files changed since previous dump. */ #define CHANGEDSINCE(dp, t) \ (DIP(dp, di_mtime) >= (t) || DIP(dp, di_ctime) >= (t)) /* The WANTTODUMP macro decides whether a file should be dumped. */ #ifdef UF_NODUMP #define WANTTODUMP(dp) \ (CHANGEDSINCE(dp, spcl.c_ddate) && \ (nonodump || (DIP(dp, di_flags) & UF_NODUMP) != UF_NODUMP)) #else #define WANTTODUMP(dp) CHANGEDSINCE(dp, spcl.c_ddate) #endif /* * Dump pass 1. * * Walk the inode list for a file system to find all allocated inodes * that have been modified since the previous dump time. Also, find all * the directories in the file system. */ int mapfiles(ino_t maxino, long *tapesize) { int i, cg, mode, inosused; int anydirskipped = 0; union dinode *dp; struct cg *cgp; ino_t ino; u_char *cp; if ((cgp = malloc(sblock->fs_cgsize)) == NULL) quit("mapfiles: cannot allocate memory.\n"); for (cg = 0; cg < sblock->fs_ncg; cg++) { ino = cg * sblock->fs_ipg; blkread(fsbtodb(sblock, cgtod(sblock, cg)), (char *)cgp, sblock->fs_cgsize); if (sblock->fs_magic == FS_UFS2_MAGIC) inosused = cgp->cg_initediblk; else inosused = sblock->fs_ipg; /* * If we are using soft updates, then we can trust the * cylinder group inode allocation maps to tell us which * inodes are allocated. We will scan the used inode map * to find the inodes that are really in use, and then * read only those inodes in from disk. */ if (sblock->fs_flags & FS_DOSOFTDEP) { if (!cg_chkmagic(cgp)) quit("mapfiles: cg %d: bad magic number\n", cg); cp = &cg_inosused(cgp)[(inosused - 1) / CHAR_BIT]; for ( ; inosused > 0; inosused -= CHAR_BIT, cp--) { if (*cp == 0) continue; for (i = 1 << (CHAR_BIT - 1); i > 0; i >>= 1) { if (*cp & i) break; inosused--; } break; } if (inosused <= 0) continue; } for (i = 0; i < inosused; i++, ino++) { if (ino < UFS_ROOTINO || (dp = getino(ino, &mode)) == NULL || (mode & IFMT) == 0) continue; if (ino >= maxino) { msg("Skipping inode %ju >= maxino %ju\n", (uintmax_t)ino, (uintmax_t)maxino); continue; } /* * Everything must go in usedinomap so that a check * for "in dumpdirmap but not in usedinomap" to detect * dirs with nodump set has a chance of succeeding * (this is used in mapdirs()). */ SETINO(ino, usedinomap); if (mode == IFDIR) SETINO(ino, dumpdirmap); if (WANTTODUMP(dp)) { SETINO(ino, dumpinomap); if (mode != IFREG && mode != IFDIR && mode != IFLNK) *tapesize += 1; else *tapesize += blockest(dp); continue; } if (mode == IFDIR) { if (!nonodump && (DIP(dp, di_flags) & UF_NODUMP)) CLRINO(ino, usedinomap); anydirskipped = 1; } } } /* * Restore gets very upset if the root is not dumped, * so ensure that it always is dumped. */ SETINO(UFS_ROOTINO, dumpinomap); return (anydirskipped); } /* * Dump pass 2. * * Scan each directory on the file system to see if it has any modified * files in it. If it does, and has not already been added to the dump * list (because it was itself modified), then add it. If a directory * has not been modified itself, contains no modified files and has no * subdirectories, then it can be deleted from the dump list and from * the list of directories. By deleting it from the list of directories, * its parent may now qualify for the same treatment on this or a later * pass using this algorithm. */ int mapdirs(ino_t maxino, long *tapesize) { union dinode *dp; int i, isdir, nodump; char *map; ino_t ino; union dinode di; long filesize; int ret, change = 0; isdir = 0; /* XXX just to get gcc to shut up */ for (map = dumpdirmap, ino = 1; ino < maxino; ino++) { if (((ino - 1) % CHAR_BIT) == 0) /* map is offset by 1 */ isdir = *map++; else isdir >>= 1; /* * If a directory has been removed from usedinomap, it * either has the nodump flag set, or has inherited * it. Although a directory can't be in dumpinomap if * it isn't in usedinomap, we have to go through it to * propagate the nodump flag. */ nodump = !nonodump && (TSTINO(ino, usedinomap) == 0); if ((isdir & 1) == 0 || (TSTINO(ino, dumpinomap) && !nodump)) continue; dp = getino(ino, &i); /* * inode buf may change in searchdir(). */ if (sblock->fs_magic == FS_UFS1_MAGIC) di.dp1 = dp->dp1; else di.dp2 = dp->dp2; filesize = DIP(&di, di_size); for (ret = 0, i = 0; filesize > 0 && i < UFS_NDADDR; i++) { if (DIP(&di, di_db[i]) != 0) ret |= searchdir(ino, DIP(&di, di_db[i]), (long)sblksize(sblock, DIP(&di, di_size), i), filesize, tapesize, nodump, maxino); if (ret & HASDUMPEDFILE) filesize = 0; else filesize -= sblock->fs_bsize; } for (i = 0; filesize > 0 && i < UFS_NIADDR; i++) { if (DIP(&di, di_ib[i]) == 0) continue; ret |= dirindir(ino, DIP(&di, di_ib[i]), i, &filesize, tapesize, nodump, maxino); } if (ret & HASDUMPEDFILE) { SETINO(ino, dumpinomap); *tapesize += blockest(&di); change = 1; continue; } if (nodump) { if (ret & HASSUBDIRS) change = 1; /* subdirs inherit nodump */ CLRINO(ino, dumpdirmap); } else if ((ret & HASSUBDIRS) == 0) if (!TSTINO(ino, dumpinomap)) { CLRINO(ino, dumpdirmap); change = 1; } } return (change); } /* * Read indirect blocks, and pass the data blocks to be searched * as directories. Quit as soon as any entry is found that will * require the directory to be dumped. */ static int dirindir( ino_t ino, ufs2_daddr_t blkno, int ind_level, long *filesize, long *tapesize, int nodump, ino_t maxino) { union { ufs1_daddr_t ufs1[MAXBSIZE / sizeof(ufs1_daddr_t)]; ufs2_daddr_t ufs2[MAXBSIZE / sizeof(ufs2_daddr_t)]; } idblk; int ret = 0; int i; blkread(fsbtodb(sblock, blkno), (char *)&idblk, (int)sblock->fs_bsize); if (ind_level <= 0) { for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) { if (sblock->fs_magic == FS_UFS1_MAGIC) blkno = idblk.ufs1[i]; else blkno = idblk.ufs2[i]; if (blkno != 0) ret |= searchdir(ino, blkno, sblock->fs_bsize, *filesize, tapesize, nodump, maxino); if (ret & HASDUMPEDFILE) *filesize = 0; else *filesize -= sblock->fs_bsize; } return (ret); } ind_level--; for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) { if (sblock->fs_magic == FS_UFS1_MAGIC) blkno = idblk.ufs1[i]; else blkno = idblk.ufs2[i]; if (blkno != 0) ret |= dirindir(ino, blkno, ind_level, filesize, tapesize, nodump, maxino); } return (ret); } /* * Scan a disk block containing directory information looking to see if * any of the entries are on the dump list and to see if the directory * contains any subdirectories. */ static int searchdir( ino_t ino, ufs2_daddr_t blkno, long size, long filesize, long *tapesize, int nodump, ino_t maxino) { int mode; struct direct *dp; union dinode *ip; long loc, ret = 0; static caddr_t dblk; if (dblk == NULL && (dblk = malloc(sblock->fs_bsize)) == NULL) quit("searchdir: cannot allocate indirect memory.\n"); blkread(fsbtodb(sblock, blkno), dblk, (int)size); if (filesize < size) size = filesize; for (loc = 0; loc < size; ) { dp = (struct direct *)(dblk + loc); if (dp->d_reclen == 0) { msg("corrupted directory, inumber %ju\n", (uintmax_t)ino); break; } loc += dp->d_reclen; if (dp->d_ino == 0) continue; if (dp->d_ino >= maxino) { msg("corrupted directory entry, d_ino %ju >= %ju\n", (uintmax_t)dp->d_ino, (uintmax_t)maxino); break; } if (dp->d_name[0] == '.') { if (dp->d_name[1] == '\0') continue; if (dp->d_name[1] == '.' && dp->d_name[2] == '\0') continue; } if (nodump) { ip = getino(dp->d_ino, &mode); if (TSTINO(dp->d_ino, dumpinomap)) { CLRINO(dp->d_ino, dumpinomap); *tapesize -= blockest(ip); } /* * Add back to dumpdirmap and remove from usedinomap * to propagate nodump. */ if (mode == IFDIR) { SETINO(dp->d_ino, dumpdirmap); CLRINO(dp->d_ino, usedinomap); ret |= HASSUBDIRS; } } else { if (TSTINO(dp->d_ino, dumpinomap)) { ret |= HASDUMPEDFILE; if (ret & HASSUBDIRS) break; } if (TSTINO(dp->d_ino, dumpdirmap)) { ret |= HASSUBDIRS; if (ret & HASDUMPEDFILE) break; } } } return (ret); } /* * Dump passes 3 and 4. * * Dump the contents of an inode to tape. */ void dumpino(union dinode *dp, ino_t ino) { int ind_level, cnt, last, added; off_t size; char buf[TP_BSIZE]; if (newtape) { newtape = 0; dumpmap(dumpinomap, TS_BITS, ino); } CLRINO(ino, dumpinomap); /* * Zero out the size of a snapshot so that it will be dumped * as a zero length file. */ if ((DIP(dp, di_flags) & SF_SNAPSHOT) != 0) { DIP_SET(dp, di_size, 0); DIP_SET(dp, di_flags, DIP(dp, di_flags) & ~SF_SNAPSHOT); } if (sblock->fs_magic == FS_UFS1_MAGIC) { spcl.c_mode = dp->dp1.di_mode; spcl.c_size = dp->dp1.di_size; spcl.c_extsize = 0; spcl.c_atime = _time32_to_time(dp->dp1.di_atime); spcl.c_atimensec = dp->dp1.di_atimensec; spcl.c_mtime = _time32_to_time(dp->dp1.di_mtime); spcl.c_mtimensec = dp->dp1.di_mtimensec; spcl.c_birthtime = 0; spcl.c_birthtimensec = 0; spcl.c_rdev = dp->dp1.di_rdev; spcl.c_file_flags = dp->dp1.di_flags; spcl.c_uid = dp->dp1.di_uid; spcl.c_gid = dp->dp1.di_gid; } else { spcl.c_mode = dp->dp2.di_mode; spcl.c_size = dp->dp2.di_size; spcl.c_extsize = dp->dp2.di_extsize; spcl.c_atime = _time64_to_time(dp->dp2.di_atime); spcl.c_atimensec = dp->dp2.di_atimensec; spcl.c_mtime = _time64_to_time(dp->dp2.di_mtime); spcl.c_mtimensec = dp->dp2.di_mtimensec; spcl.c_birthtime = _time64_to_time(dp->dp2.di_birthtime); spcl.c_birthtimensec = dp->dp2.di_birthnsec; spcl.c_rdev = dp->dp2.di_rdev; spcl.c_file_flags = dp->dp2.di_flags; spcl.c_uid = dp->dp2.di_uid; spcl.c_gid = dp->dp2.di_gid; } spcl.c_type = TS_INODE; spcl.c_count = 0; switch (DIP(dp, di_mode) & S_IFMT) { case 0: /* * Freed inode. */ return; case S_IFLNK: /* * Check for short symbolic link. */ if (DIP(dp, di_size) > 0 && DIP(dp, di_size) < sblock->fs_maxsymlinklen) { spcl.c_addr[0] = 1; spcl.c_count = 1; added = appendextdata(dp); writeheader(ino); memmove(buf, DIP(dp, di_shortlink), (u_long)DIP(dp, di_size)); buf[DIP(dp, di_size)] = '\0'; writerec(buf, 0); writeextdata(dp, ino, added); return; } /* FALLTHROUGH */ case S_IFDIR: case S_IFREG: if (DIP(dp, di_size) > 0) break; /* FALLTHROUGH */ case S_IFIFO: case S_IFSOCK: case S_IFCHR: case S_IFBLK: added = appendextdata(dp); writeheader(ino); writeextdata(dp, ino, added); return; default: msg("Warning: undefined file type 0%o\n", DIP(dp, di_mode) & IFMT); return; } if (DIP(dp, di_size) > UFS_NDADDR * sblock->fs_bsize) { cnt = UFS_NDADDR * sblock->fs_frag; last = 0; } else { cnt = howmany(DIP(dp, di_size), sblock->fs_fsize); last = 1; } if (sblock->fs_magic == FS_UFS1_MAGIC) ufs1_blksout(&dp->dp1.di_db[0], cnt, ino); else ufs2_blksout(dp, &dp->dp2.di_db[0], cnt, ino, last); if ((size = DIP(dp, di_size) - UFS_NDADDR * sblock->fs_bsize) <= 0) return; for (ind_level = 0; ind_level < UFS_NIADDR; ind_level++) { dmpindir(dp, ino, DIP(dp, di_ib[ind_level]), ind_level, &size); if (size <= 0) return; } } /* * Read indirect blocks, and pass the data blocks to be dumped. */ static void dmpindir(union dinode *dp, ino_t ino, ufs2_daddr_t blk, int ind_level, off_t *size) { union { ufs1_daddr_t ufs1[MAXBSIZE / sizeof(ufs1_daddr_t)]; ufs2_daddr_t ufs2[MAXBSIZE / sizeof(ufs2_daddr_t)]; } idblk; int i, cnt, last; if (blk != 0) blkread(fsbtodb(sblock, blk), (char *)&idblk, (int)sblock->fs_bsize); else memset(&idblk, 0, sblock->fs_bsize); if (ind_level <= 0) { if (*size > NINDIR(sblock) * sblock->fs_bsize) { cnt = NINDIR(sblock) * sblock->fs_frag; last = 0; } else { cnt = howmany(*size, sblock->fs_fsize); last = 1; } *size -= NINDIR(sblock) * sblock->fs_bsize; if (sblock->fs_magic == FS_UFS1_MAGIC) ufs1_blksout(idblk.ufs1, cnt, ino); else ufs2_blksout(dp, idblk.ufs2, cnt, ino, last); return; } ind_level--; for (i = 0; i < NINDIR(sblock); i++) { if (sblock->fs_magic == FS_UFS1_MAGIC) dmpindir(dp, ino, idblk.ufs1[i], ind_level, size); else dmpindir(dp, ino, idblk.ufs2[i], ind_level, size); if (*size <= 0) return; } } /* * Collect up the data into tape record sized buffers and output them. */ static void ufs1_blksout(ufs1_daddr_t *blkp, int frags, ino_t ino) { ufs1_daddr_t *bp; int i, j, count, blks, tbperdb; blks = howmany(frags * sblock->fs_fsize, TP_BSIZE); tbperdb = sblock->fs_bsize >> tp_bshift; for (i = 0; i < blks; i += TP_NINDIR) { if (i + TP_NINDIR > blks) count = blks; else count = i + TP_NINDIR; assert(count <= TP_NINDIR + i); for (j = i; j < count; j++) if (blkp[j / tbperdb] != 0) spcl.c_addr[j - i] = 1; else spcl.c_addr[j - i] = 0; spcl.c_count = count - i; writeheader(ino); bp = &blkp[i / tbperdb]; for (j = i; j < count; j += tbperdb, bp++) if (*bp != 0) { if (j + tbperdb <= count) dumpblock(*bp, (int)sblock->fs_bsize); else dumpblock(*bp, (count - j) * TP_BSIZE); } spcl.c_type = TS_ADDR; } } /* * Collect up the data into tape record sized buffers and output them. */ static void ufs2_blksout(union dinode *dp, ufs2_daddr_t *blkp, int frags, ino_t ino, int last) { ufs2_daddr_t *bp; int i, j, count, resid, blks, tbperdb, added; static int writingextdata = 0; /* * Calculate the number of TP_BSIZE blocks to be dumped. * For filesystems with a fragment size bigger than TP_BSIZE, * only part of the final fragment may need to be dumped. */ blks = howmany(frags * sblock->fs_fsize, TP_BSIZE); if (last) { if (writingextdata) resid = howmany(fragoff(sblock, spcl.c_extsize), TP_BSIZE); else resid = howmany(fragoff(sblock, dp->dp2.di_size), TP_BSIZE); if (resid > 0) blks -= howmany(sblock->fs_fsize, TP_BSIZE) - resid; } tbperdb = sblock->fs_bsize >> tp_bshift; for (i = 0; i < blks; i += TP_NINDIR) { if (i + TP_NINDIR > blks) count = blks; else count = i + TP_NINDIR; assert(count <= TP_NINDIR + i); for (j = i; j < count; j++) if (blkp[j / tbperdb] != 0) spcl.c_addr[j - i] = 1; else spcl.c_addr[j - i] = 0; spcl.c_count = count - i; if (last && count == blks && !writingextdata) added = appendextdata(dp); writeheader(ino); bp = &blkp[i / tbperdb]; for (j = i; j < count; j += tbperdb, bp++) if (*bp != 0) { if (j + tbperdb <= count) dumpblock(*bp, (int)sblock->fs_bsize); else dumpblock(*bp, (count - j) * TP_BSIZE); } spcl.c_type = TS_ADDR; spcl.c_count = 0; if (last && count == blks && !writingextdata) { writingextdata = 1; writeextdata(dp, ino, added); writingextdata = 0; } } } /* * If there is room in the current block for the extended attributes * as well as the file data, update the header to reflect the added * attribute data at the end. Attributes are placed at the end so that * old versions of restore will correctly restore the file and simply * discard the extra data at the end that it does not understand. * The attribute data is dumped following the file data by the * writeextdata() function (below). */ static int appendextdata(union dinode *dp) { int i, blks, tbperdb; /* * If no extended attributes, there is nothing to do. */ if (spcl.c_extsize == 0) return (0); /* * If there is not enough room at the end of this block * to add the extended attributes, then rather than putting * part of them here, we simply push them entirely into a * new block rather than putting some here and some later. */ if (spcl.c_extsize > UFS_NXADDR * sblock->fs_bsize) blks = howmany(UFS_NXADDR * sblock->fs_bsize, TP_BSIZE); else blks = howmany(spcl.c_extsize, TP_BSIZE); if (spcl.c_count + blks > TP_NINDIR) return (0); /* * Update the block map in the header to indicate the added * extended attribute. They will be appended after the file * data by the writeextdata() routine. */ tbperdb = sblock->fs_bsize >> tp_bshift; assert(spcl.c_count + blks <= TP_NINDIR); for (i = 0; i < blks; i++) if (&dp->dp2.di_extb[i / tbperdb] != 0) spcl.c_addr[spcl.c_count + i] = 1; else spcl.c_addr[spcl.c_count + i] = 0; spcl.c_count += blks; return (blks); } /* * Dump the extended attribute data. If there was room in the file * header, then all we need to do is output the data blocks. If there * was not room in the file header, then an additional TS_ADDR header * is created to hold the attribute data. */ static void writeextdata(union dinode *dp, ino_t ino, int added) { int i, frags, blks, tbperdb, last; ufs2_daddr_t *bp; off_t size; /* * If no extended attributes, there is nothing to do. */ if (spcl.c_extsize == 0) return; /* * If there was no room in the file block for the attributes, * dump them out in a new block, otherwise just dump the data. */ if (added == 0) { if (spcl.c_extsize > UFS_NXADDR * sblock->fs_bsize) { frags = UFS_NXADDR * sblock->fs_frag; last = 0; } else { frags = howmany(spcl.c_extsize, sblock->fs_fsize); last = 1; } ufs2_blksout(dp, &dp->dp2.di_extb[0], frags, ino, last); } else { if (spcl.c_extsize > UFS_NXADDR * sblock->fs_bsize) blks = howmany(UFS_NXADDR * sblock->fs_bsize, TP_BSIZE); else blks = howmany(spcl.c_extsize, TP_BSIZE); tbperdb = sblock->fs_bsize >> tp_bshift; for (i = 0; i < blks; i += tbperdb) { bp = &dp->dp2.di_extb[i / tbperdb]; if (*bp != 0) { if (i + tbperdb <= blks) dumpblock(*bp, (int)sblock->fs_bsize); else dumpblock(*bp, (blks - i) * TP_BSIZE); } } } /* * If an indirect block is added for extended attributes, then * di_exti below should be changed to the structure element * that references the extended attribute indirect block. This * definition is here only to make it compile without complaint. */ #define di_exti di_spare[0] /* * If the extended attributes fall into an indirect block, * dump it as well. */ if ((size = spcl.c_extsize - UFS_NXADDR * sblock->fs_bsize) > 0) dmpindir(dp, ino, dp->dp2.di_exti, 0, &size); } /* * Dump a map to the tape. */ void dumpmap(char *map, int type, ino_t ino) { int i; char *cp; spcl.c_type = type; spcl.c_count = howmany(mapsize * sizeof(char), TP_BSIZE); writeheader(ino); for (i = 0, cp = map; i < spcl.c_count; i++, cp += TP_BSIZE) writerec(cp, 0); } /* * Write a header record to the dump tape. */ void writeheader(ino_t ino) { int32_t sum, cnt, *lp; if (rsync_friendly >= 2) { /* don't track changes to access time */ spcl.c_atime = spcl.c_mtime; spcl.c_atimensec = spcl.c_mtimensec; } spcl.c_inumber = ino; spcl.c_magic = FS_UFS2_MAGIC; spcl.c_checksum = 0; lp = (int32_t *)&spcl; sum = 0; cnt = sizeof(union u_spcl) / (4 * sizeof(int32_t)); while (--cnt >= 0) { sum += *lp++; sum += *lp++; sum += *lp++; sum += *lp++; } spcl.c_checksum = CHECKSUM - sum; writerec((char *)&spcl, 1); } union dinode * getino(ino_t inum, int *modep) { static ino_t minino, maxino; static caddr_t inoblock; struct ufs1_dinode *dp1; struct ufs2_dinode *dp2; if (inoblock == NULL && (inoblock = malloc(sblock->fs_bsize)) == NULL) quit("cannot allocate inode memory.\n"); curino = inum; if (inum >= minino && inum < maxino) goto gotit; blkread(fsbtodb(sblock, ino_to_fsba(sblock, inum)), inoblock, (int)sblock->fs_bsize); minino = inum - (inum % INOPB(sblock)); maxino = minino + INOPB(sblock); gotit: if (sblock->fs_magic == FS_UFS1_MAGIC) { dp1 = &((struct ufs1_dinode *)inoblock)[inum - minino]; *modep = (dp1->di_mode & IFMT); return ((union dinode *)dp1); } dp2 = &((struct ufs2_dinode *)inoblock)[inum - minino]; *modep = (dp2->di_mode & IFMT); return ((union dinode *)dp2); } /* * Read a chunk of data from the disk. * Try to recover from hard errors by reading in sector sized pieces. * Error recovery is attempted at most BREADEMAX times before seeking * consent from the operator to continue. */ int breaderrors = 0; #define BREADEMAX 32 void blkread(ufs2_daddr_t blkno, char *buf, int size) { int secsize, bytes, resid, xfer, base, cnt, i; static char *tmpbuf; off_t offset; loop: offset = blkno << dev_bshift; secsize = sblock->fs_fsize; base = offset % secsize; resid = size % secsize; /* * If the transfer request starts or ends on a non-sector * boundary, we must read the entire sector and copy out * just the part that we need. */ if (base == 0 && resid == 0) { cnt = cread(diskfd, buf, size, offset); if (cnt == size) return; } else { if (tmpbuf == NULL && (tmpbuf = malloc(secsize)) == NULL) quit("buffer malloc failed\n"); xfer = 0; bytes = size; if (base != 0) { cnt = cread(diskfd, tmpbuf, secsize, offset - base); if (cnt != secsize) goto bad; xfer = MIN(secsize - base, size); offset += xfer; bytes -= xfer; resid = bytes % secsize; memcpy(buf, &tmpbuf[base], xfer); } if (bytes >= secsize) { cnt = cread(diskfd, &buf[xfer], bytes - resid, offset); if (cnt != bytes - resid) goto bad; xfer += cnt; offset += cnt; } if (resid == 0) return; cnt = cread(diskfd, tmpbuf, secsize, offset); if (cnt == secsize) { memcpy(&buf[xfer], tmpbuf, resid); return; } } bad: if (blkno + (size / dev_bsize) > fsbtodb(sblock, sblock->fs_size)) { /* * Trying to read the final fragment. * * NB - dump only works in TP_BSIZE blocks, hence * rounds `dev_bsize' fragments up to TP_BSIZE pieces. * It should be smarter about not actually trying to * read more than it can get, but for the time being * we punt and scale back the read only when it gets * us into trouble. (mkm 9/25/83) */ size -= dev_bsize; goto loop; } if (cnt == -1) msg("read error from %s: %s: [block %jd]: count=%d\n", disk, strerror(errno), (intmax_t)blkno, size); else msg("short read error from %s: [block %jd]: count=%d, got=%d\n", disk, (intmax_t)blkno, size, cnt); if (++breaderrors > BREADEMAX) { msg("More than %d block read errors from %s\n", BREADEMAX, disk); broadcast("DUMP IS AILING!\n"); msg("This is an unrecoverable error.\n"); if (!query("Do you want to attempt to continue?")){ dumpabort(0); /*NOTREACHED*/ } else breaderrors = 0; } /* * Zero buffer, then try to read each sector of buffer separately, * and bypass the cache. */ memset(buf, 0, size); for (i = 0; i < size; i += dev_bsize, buf += dev_bsize, blkno++) { if ((cnt = pread(diskfd, buf, (int)dev_bsize, ((off_t)blkno << dev_bshift))) == dev_bsize) continue; if (cnt == -1) { msg("read error from %s: %s: [sector %jd]: count=%ld\n", disk, strerror(errno), (intmax_t)blkno, dev_bsize); continue; } msg("short read from %s: [sector %jd]: count=%ld, got=%d\n", disk, (intmax_t)blkno, dev_bsize, cnt); } } diff --git a/sbin/dump/unctime.c b/sbin/dump/unctime.c index 3e03a65f1a7c..5f44c80f9358 100644 --- a/sbin/dump/unctime.c +++ b/sbin/dump/unctime.c @@ -1,58 +1,56 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)unctime.c 8.2 (Berkeley) 6/14/94"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include /* * Convert a ctime(3) format string into a system format date. * Return the date thus calculated. * * Return -1 if the string is not in ctime format. */ time_t unctime(char *str) { struct tm then; str = strptime(str, "%a %b %e %T %Y", &then); if (str == NULL || (*str != '\n' && *str != '\0')) return ((time_t)-1); then.tm_isdst = -1; return (mktime(&then)); } diff --git a/sbin/dumpfs/dumpfs.c b/sbin/dumpfs/dumpfs.c index 739f281feb7f..7d2598c190e8 100644 --- a/sbin/dumpfs/dumpfs.c +++ b/sbin/dumpfs/dumpfs.c @@ -1,548 +1,546 @@ /* * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2009 Robert N. M. Watson * All rights reserved. * * This software was developed at the University of Cambridge Computer * Laboratory with support from a grant from Google, Inc. * * Copyright (c) 2002 Networks Associates Technology, Inc. * All rights reserved. * * This software was developed for the FreeBSD Project by Marshall * Kirk McKusick and Network Associates Laboratories, the Security * Research Division of Network Associates, Inc. under DARPA/SPAWAR * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS * research program. * * Copyright (c) 1983, 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1983, 1992, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)dumpfs.c 8.5 (Berkeley) 4/29/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define afs disk.d_fs #define acg disk.d_cg static struct uufsd disk; static int dumpfs(const char *, int); static int dumpfsid(void); static int dumpcg(void); static int dumpfreespace(const char *, int); static void dumpfreespacecg(int); static int marshal(const char *); static void pbits(void *, int); static void pblklist(void *, int, off_t, int); static const char *ufserr(void); static void usage(void) __dead2; int main(int argc, char *argv[]) { const char *name; int ch, dofreespace, domarshal, dolabel, dosb, eval; dofreespace = domarshal = dolabel = dosb = eval = 0; while ((ch = getopt(argc, argv, "lfms")) != -1) { switch (ch) { case 'f': dofreespace++; break; case 'm': domarshal = 1; break; case 'l': dolabel = 1; break; case 's': dosb = 1; break; case '?': default: usage(); } } argc -= optind; argv += optind; if (argc < 1) usage(); if (dofreespace && domarshal) usage(); if (dofreespace > 2) usage(); while ((name = *argv++) != NULL) { if (ufs_disk_fillout_blank(&disk, name) == -1 || sbfind(&disk, 0) == -1) { printf("\n%s: %s\n", name, ufserr()); eval |= 1; continue; } if (dofreespace) eval |= dumpfreespace(name, dofreespace); else if (domarshal) eval |= marshal(name); else if (dolabel) eval |= dumpfsid(); else eval |= dumpfs(name, dosb); ufs_disk_close(&disk); } exit(eval); } static int dumpfsid(void) { printf("%sufsid/%08x%08x\n", _PATH_DEV, afs.fs_id[0], afs.fs_id[1]); return 0; } static int dumpfs(const char *name, int dosb) { time_t fstime, fsmtime; int64_t fssize; int32_t fsflags; int i, ret; switch (disk.d_ufs) { case 2: fssize = afs.fs_size; fstime = afs.fs_time; fsmtime = afs.fs_mtime; printf("magic\t%x (UFS2)\n", afs.fs_magic); printf("last mounted time\t%s", ctime(&fsmtime)); printf("last modified time\t%s", ctime(&fstime)); printf("superblock location\t%jd\tid\t[ %08x %08x ]\n", (intmax_t)afs.fs_sblockloc, afs.fs_id[0], afs.fs_id[1]); printf("ncg\t%d\tsize\t%jd\tblocks\t%jd\n", afs.fs_ncg, (intmax_t)fssize, (intmax_t)afs.fs_dsize); break; case 1: fssize = afs.fs_old_size; fstime = afs.fs_old_time; printf("magic\t%x (UFS1)\ttime\t%s", afs.fs_magic, ctime(&fstime)); printf("id\t[ %08x %08x ]\n", afs.fs_id[0], afs.fs_id[1]); printf("ncg\t%d\tsize\t%jd\tblocks\t%jd\n", afs.fs_ncg, (intmax_t)fssize, (intmax_t)afs.fs_dsize); break; default: printf("Unknown filesystem type %d\n", disk.d_ufs); return (1); } printf("bsize\t%d\tshift\t%d\tmask\t0x%08x\n", afs.fs_bsize, afs.fs_bshift, afs.fs_bmask); printf("fsize\t%d\tshift\t%d\tmask\t0x%08x\n", afs.fs_fsize, afs.fs_fshift, afs.fs_fmask); printf("frag\t%d\tshift\t%d\tfsbtodb\t%d\n", afs.fs_frag, afs.fs_fragshift, afs.fs_fsbtodb); printf("minfree\t%d%%\toptim\t%s\tsymlinklen %d\n", afs.fs_minfree, afs.fs_optim == FS_OPTSPACE ? "space" : "time", afs.fs_maxsymlinklen); switch (disk.d_ufs) { case 2: printf("%s %d\tmaxbpg\t%d\tmaxcontig %d\tcontigsumsize %d\n", "maxbsize", afs.fs_maxbsize, afs.fs_maxbpg, afs.fs_maxcontig, afs.fs_contigsumsize); printf("nbfree\t%jd\tndir\t%jd\tnifree\t%jd\tnffree\t%jd\n", (intmax_t)afs.fs_cstotal.cs_nbfree, (intmax_t)afs.fs_cstotal.cs_ndir, (intmax_t)afs.fs_cstotal.cs_nifree, (intmax_t)afs.fs_cstotal.cs_nffree); printf("bpg\t%d\tfpg\t%d\tipg\t%d\tunrefs\t%jd\n", afs.fs_fpg / afs.fs_frag, afs.fs_fpg, afs.fs_ipg, (intmax_t)afs.fs_unrefs); printf("nindir\t%d\tinopb\t%d\tmaxfilesize\t%ju\n", afs.fs_nindir, afs.fs_inopb, (uintmax_t)afs.fs_maxfilesize); printf("sbsize\t%d\tcgsize\t%d\tcsaddr\t%jd\tcssize\t%d\n", afs.fs_sbsize, afs.fs_cgsize, (intmax_t)afs.fs_csaddr, afs.fs_cssize); break; case 1: printf("maxbpg\t%d\tmaxcontig %d\tcontigsumsize %d\n", afs.fs_maxbpg, afs.fs_maxcontig, afs.fs_contigsumsize); printf("nbfree\t%d\tndir\t%d\tnifree\t%d\tnffree\t%d\n", afs.fs_old_cstotal.cs_nbfree, afs.fs_old_cstotal.cs_ndir, afs.fs_old_cstotal.cs_nifree, afs.fs_old_cstotal.cs_nffree); printf("cpg\t%d\tbpg\t%d\tfpg\t%d\tipg\t%d\n", afs.fs_old_cpg, afs.fs_fpg / afs.fs_frag, afs.fs_fpg, afs.fs_ipg); printf("nindir\t%d\tinopb\t%d\tnspf\t%d\tmaxfilesize\t%ju\n", afs.fs_nindir, afs.fs_inopb, afs.fs_old_nspf, (uintmax_t)afs.fs_maxfilesize); printf("sbsize\t%d\tcgsize\t%d\tcgoffset %d\tcgmask\t0x%08x\n", afs.fs_sbsize, afs.fs_cgsize, afs.fs_old_cgoffset, afs.fs_old_cgmask); printf("csaddr\t%jd\tcssize\t%d\n", (intmax_t)afs.fs_csaddr, afs.fs_cssize); printf("rotdelay %dms\trps\t%d\ttrackskew %d\tinterleave %d\n", afs.fs_old_rotdelay, afs.fs_old_rps, afs.fs_old_trackskew, afs.fs_old_interleave); printf("nsect\t%d\tnpsect\t%d\tspc\t%d\n", afs.fs_old_nsect, afs.fs_old_npsect, afs.fs_old_spc); break; default: printf("Unknown filesystem type %d\n", disk.d_ufs); return (1); } printf("old_cpg\t%d\tsize_cg\t%zu\tCGSIZE\t%zu\n", afs.fs_old_cpg, sizeof(struct cg), CGSIZE(&afs)); printf("sblkno\t%d\tcblkno\t%d\tiblkno\t%d\tdblkno\t%d\n", afs.fs_sblkno, afs.fs_cblkno, afs.fs_iblkno, afs.fs_dblkno); printf("cgrotor\t%d\tfmod\t%d\tronly\t%d\tclean\t%d\n", afs.fs_cgrotor, afs.fs_fmod, afs.fs_ronly, afs.fs_clean); printf("metaspace %jd\tavgfpdir %d\tavgfilesize %d\n", afs.fs_metaspace, afs.fs_avgfpdir, afs.fs_avgfilesize); printf("flags\t"); if (afs.fs_old_flags & FS_FLAGS_UPDATED) fsflags = afs.fs_flags; else fsflags = afs.fs_old_flags; if (fsflags == 0) printf("none"); if (fsflags & FS_UNCLEAN) printf("unclean "); if (fsflags & FS_DOSOFTDEP) printf("soft-updates%s ", (fsflags & FS_SUJ) ? "+journal" : ""); if (fsflags & FS_NEEDSFSCK) printf("needs-fsck-run "); if (fsflags & FS_INDEXDIRS) printf("indexed-directories "); if (fsflags & FS_ACLS) printf("acls "); if (fsflags & FS_MULTILABEL) printf("multilabel "); if (fsflags & FS_GJOURNAL) printf("gjournal "); if (fsflags & FS_FLAGS_UPDATED) printf("fs_flags-expanded "); if (fsflags & FS_NFS4ACLS) printf("nfsv4acls "); if (fsflags & FS_TRIM) printf("trim "); fsflags &= ~(FS_UNCLEAN | FS_DOSOFTDEP | FS_NEEDSFSCK | FS_METACKHASH | FS_ACLS | FS_MULTILABEL | FS_GJOURNAL | FS_FLAGS_UPDATED | FS_NFS4ACLS | FS_SUJ | FS_TRIM | FS_INDEXDIRS); if (fsflags != 0) printf("unknown-flags (%#x)", fsflags); putchar('\n'); if (afs.fs_flags & FS_METACKHASH) { printf("check hashes\t"); fsflags = afs.fs_metackhash; if (fsflags == 0) printf("none"); if (fsflags & CK_SUPERBLOCK) printf("superblock "); if (fsflags & CK_CYLGRP) printf("cylinder-groups "); if (fsflags & CK_INODE) printf("inodes "); if (fsflags & CK_INDIR) printf("indirect-blocks "); if (fsflags & CK_DIR) printf("directories "); } fsflags &= ~(CK_SUPERBLOCK | CK_CYLGRP | CK_INODE | CK_INDIR | CK_DIR); if (fsflags != 0) printf("unknown flags (%#x)", fsflags); putchar('\n'); printf("fsmnt\t%s\n", afs.fs_fsmnt); printf("volname\t%s\tswuid\t%ju\tprovidersize\t%ju\n", afs.fs_volname, (uintmax_t)afs.fs_swuid, (uintmax_t)afs.fs_providersize); printf("\ncs[].cs_(nbfree,ndir,nifree,nffree):\n\t"); for (i = 0; i < afs.fs_ncg; i++) { struct csum *cs = &afs.fs_cs(&afs, i); if (i && i % 4 == 0) printf("\n\t"); printf("(%d,%d,%d,%d) ", cs->cs_nbfree, cs->cs_ndir, cs->cs_nifree, cs->cs_nffree); } printf("\n"); if (fssize % afs.fs_fpg) { if (disk.d_ufs == 1) printf("cylinders in last group %d\n", howmany(afs.fs_old_size % afs.fs_fpg, afs.fs_old_spc / afs.fs_old_nspf)); printf("blocks in last group %ld\n\n", (long)((fssize % afs.fs_fpg) / afs.fs_frag)); } if (dosb) return (0); ret = 0; while ((i = cgread(&disk)) != 0) { if (i == -1) { ret = 1; printf("\ncg %d: %s\n", disk.d_lcg, ufserr()); } else if (dumpcg()) ret = 1; } return (ret); } static int dumpcg(void) { time_t cgtime; off_t cur; int i, j; printf("\ncg %d:\n", disk.d_lcg); cur = fsbtodb(&afs, cgtod(&afs, disk.d_lcg)) * disk.d_bsize; switch (disk.d_ufs) { case 2: cgtime = acg.cg_time; printf("magic\t%x\ttell\t%jx\ttime\t%s", acg.cg_magic, (intmax_t)cur, ctime(&cgtime)); printf("cgx\t%d\tndblk\t%d\tniblk\t%d\tinitiblk %d\tunrefs %d\n", acg.cg_cgx, acg.cg_ndblk, acg.cg_niblk, acg.cg_initediblk, acg.cg_unrefs); break; case 1: cgtime = acg.cg_old_time; printf("magic\t%x\ttell\t%jx\ttime\t%s", acg.cg_magic, (intmax_t)cur, ctime(&cgtime)); printf("cgx\t%d\tncyl\t%d\tniblk\t%d\tndblk\t%d\n", acg.cg_cgx, acg.cg_old_ncyl, acg.cg_old_niblk, acg.cg_ndblk); break; default: break; } printf("nbfree\t%d\tndir\t%d\tnifree\t%d\tnffree\t%d\n", acg.cg_cs.cs_nbfree, acg.cg_cs.cs_ndir, acg.cg_cs.cs_nifree, acg.cg_cs.cs_nffree); printf("rotor\t%d\tirotor\t%d\tfrotor\t%d\nfrsum", acg.cg_rotor, acg.cg_irotor, acg.cg_frotor); for (i = 1, j = 0; i < afs.fs_frag; i++) { printf("\t%d", acg.cg_frsum[i]); j += i * acg.cg_frsum[i]; } printf("\nsum of frsum: %d", j); if (afs.fs_contigsumsize > 0) { for (i = 1; i < afs.fs_contigsumsize; i++) { if ((i - 1) % 8 == 0) printf("\nclusters %d-%d:", i, MIN(afs.fs_contigsumsize - 1, i + 7)); printf("\t%d", cg_clustersum(&acg)[i]); } printf("\nclusters size %d and over: %d\n", afs.fs_contigsumsize, cg_clustersum(&acg)[afs.fs_contigsumsize]); printf("clusters free:\t"); pbits(cg_clustersfree(&acg), acg.cg_nclusterblks); } else printf("\n"); printf("inodes used:\t"); pbits(cg_inosused(&acg), afs.fs_ipg); printf("blks free:\t"); pbits(cg_blksfree(&acg), afs.fs_fpg); return (0); } static int dumpfreespace(const char *name, int fflag) { intmax_t startblkno; int i, ret; ret = 0; while ((i = cgread(&disk)) != 0) { if (i != -1) { dumpfreespacecg(fflag); } else { startblkno = disk.d_lcg * afs.fs_fpg; printf("\nBlocks %jd-%jd of cg %d skipped: %s\n", startblkno, startblkno + afs.fs_fpg - 1, disk.d_lcg, ufserr()); ret = 1; } } return (ret); } static void dumpfreespacecg(int fflag) { pblklist(cg_blksfree(&acg), afs.fs_fpg, disk.d_lcg * afs.fs_fpg, fflag); } static int marshal(const char *name) { struct fs *fs; fs = &disk.d_fs; printf("# newfs command for %s (%s)\n", name, disk.d_name); printf("newfs "); if (fs->fs_volname[0] != '\0') printf("-L %s ", fs->fs_volname); printf("-O %d ", disk.d_ufs); if (fs->fs_flags & FS_DOSOFTDEP) printf("-U "); printf("-a %d ", fs->fs_maxcontig); printf("-b %d ", fs->fs_bsize); /* -c is dumb */ printf("-d %d ", fs->fs_maxbsize); printf("-e %d ", fs->fs_maxbpg); printf("-f %d ", fs->fs_fsize); printf("-g %d ", fs->fs_avgfilesize); printf("-h %d ", fs->fs_avgfpdir); printf("-i %jd ", fragroundup(fs, lblktosize(fs, fragstoblks(fs, fs->fs_fpg)) / fs->fs_ipg)); if (fs->fs_flags & FS_SUJ) printf("-j "); if (fs->fs_flags & FS_GJOURNAL) printf("-J "); printf("-k %jd ", fs->fs_metaspace); if (fs->fs_flags & FS_MULTILABEL) printf("-l "); printf("-m %d ", fs->fs_minfree); /* -n unimplemented */ printf("-o "); switch (fs->fs_optim) { case FS_OPTSPACE: printf("space "); break; case FS_OPTTIME: printf("time "); break; default: printf("unknown "); break; } /* -p..r unimplemented */ printf("-s %jd ", (intmax_t)fsbtodb(fs, fs->fs_size)); if (fs->fs_flags & FS_TRIM) printf("-t "); printf("%s ", disk.d_name); printf("\n"); return 0; } static void pbits(void *vp, int max) { int i; char *p; int count, j; for (count = i = 0, p = vp; i < max; i++) if (isset(p, i)) { if (count) printf(",%s", count % 6 ? " " : "\n\t"); count++; printf("%d", i); j = i; while ((i+1) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "debug.h" /* *********************************************************** GLOBALS ***** */ #ifdef FS_DEBUG int _dbg_lvl_ = (DL_INFO); /* DL_TRC */ #endif /* FS_DEBUG */ static struct uufsd disk; #define sblock disk.d_fs #define acg disk.d_cg static union { struct fs fs; char pad[SBLOCKSIZE]; } fsun; #define osblock fsun.fs static char i1blk[MAXBSIZE]; static char i2blk[MAXBSIZE]; static char i3blk[MAXBSIZE]; static struct csum *fscs; /* ******************************************************** PROTOTYPES ***** */ static void usage(void); static void dump_whole_ufs1_inode(ino_t, int); static void dump_whole_ufs2_inode(ino_t, int); #define DUMP_WHOLE_INODE(A,B) \ ( disk.d_ufs == 1 \ ? dump_whole_ufs1_inode((A),(B)) : dump_whole_ufs2_inode((A),(B)) ) /* ************************************************************** main ***** */ /* * ffsinfo(8) is a tool to dump all metadata of a file system. It helps to find * errors is the file system much easier. You can run ffsinfo before and after * an fsck(8), and compare the two ascii dumps easy with diff, and you see * directly where the problem is. You can control how much detail you want to * see with some command line arguments. You can also easy check the status * of a file system, like is there is enough space for growing a file system, * or how many active snapshots do we have. It provides much more detailed * information then dumpfs. Snapshots, as they are very new, are not really * supported. They are just mentioned currently, but it is planned to run * also over active snapshots, to even get that output. */ int main(int argc, char **argv) { DBG_FUNC("main") char *device, *special; int ch; size_t len; struct stat st; struct csum *dbg_csp; int dbg_csc; char dbg_line[80]; int cylno,i; int cfg_cg, cfg_in, cfg_lv; int cg_start, cg_stop; ino_t in; char *out_file; DBG_ENTER; cfg_lv = 0xff; cfg_in = -2; cfg_cg = -2; out_file = strdup("-"); while ((ch = getopt(argc, argv, "g:i:l:o:")) != -1) { switch (ch) { case 'g': cfg_cg = strtol(optarg, NULL, 0); if (errno == EINVAL || errno == ERANGE) err(1, "%s", optarg); if (cfg_cg < -1) usage(); break; case 'i': cfg_in = strtol(optarg, NULL, 0); if (errno == EINVAL || errno == ERANGE) err(1, "%s", optarg); if (cfg_in < 0) usage(); break; case 'l': cfg_lv = strtol(optarg, NULL, 0); if (errno == EINVAL||errno == ERANGE) err(1, "%s", optarg); if (cfg_lv < 0x1 || cfg_lv > 0x3ff) usage(); break; case 'o': free(out_file); out_file = strdup(optarg); if (out_file == NULL) errx(1, "strdup failed"); break; case '?': /* FALLTHROUGH */ default: usage(); } } argc -= optind; argv += optind; if (argc != 1) usage(); device = *argv; /* * Now we try to guess the (raw)device name. */ if (0 == strrchr(device, '/') && stat(device, &st) == -1) { /*- * No path prefix was given, so try in this order: * /dev/r%s * /dev/%s * /dev/vinum/r%s * /dev/vinum/%s. * * FreeBSD now doesn't distinguish between raw and block * devices any longer, but it should still work this way. */ len = strlen(device) + strlen(_PATH_DEV) + 2 + strlen("vinum/"); special = (char *)malloc(len); if (special == NULL) errx(1, "malloc failed"); snprintf(special, len, "%sr%s", _PATH_DEV, device); if (stat(special, &st) == -1) { snprintf(special, len, "%s%s", _PATH_DEV, device); if (stat(special, &st) == -1) { snprintf(special, len, "%svinum/r%s", _PATH_DEV, device); if (stat(special, &st) == -1) /* For now this is the 'last resort' */ snprintf(special, len, "%svinum/%s", _PATH_DEV, device); } } device = special; } if (ufs_disk_fillout_blank(&disk, device) == -1 || sbfind(&disk, 0) == -1) err(1, "superblock fetch(%s) failed: %s", device, disk.d_error); DBG_OPEN(out_file); /* already here we need a superblock */ if (cfg_lv & 0x001) DBG_DUMP_FS(&sblock, "primary sblock"); /* Determine here what cylinder groups to dump */ if (cfg_cg==-2) { cg_start = 0; cg_stop = sblock.fs_ncg; } else if (cfg_cg == -1) { cg_start = sblock.fs_ncg - 1; cg_stop = sblock.fs_ncg; } else if (cfg_cg < sblock.fs_ncg) { cg_start = cfg_cg; cg_stop = cfg_cg + 1; } else { cg_start = sblock.fs_ncg; cg_stop = sblock.fs_ncg; } if (cfg_lv & 0x004) { fscs = (struct csum *)calloc((size_t)1, (size_t)sblock.fs_cssize); if (fscs == NULL) errx(1, "calloc failed"); /* get the cylinder summary into the memory ... */ for (i = 0; i < sblock.fs_cssize; i += sblock.fs_bsize) { if (bread(&disk, fsbtodb(&sblock, sblock.fs_csaddr + numfrags(&sblock, i)), (void *)(((char *)fscs)+i), (size_t)(sblock.fs_cssize-i < sblock.fs_bsize ? sblock.fs_cssize - i : sblock.fs_bsize)) == -1) err(1, "bread: %s", disk.d_error); } dbg_csp = fscs; /* ... and dump it */ for (dbg_csc = 0; dbg_csc < sblock.fs_ncg; dbg_csc++) { snprintf(dbg_line, sizeof(dbg_line), "%d. csum in fscs", dbg_csc); DBG_DUMP_CSUM(&sblock, dbg_line, dbg_csp++); } } if (cfg_lv & 0xf8) { /* for each requested cylinder group ... */ for (cylno = cg_start; cylno < cg_stop; cylno++) { snprintf(dbg_line, sizeof(dbg_line), "cgr %d", cylno); if (cfg_lv & 0x002) { /* dump the superblock copies */ if (bread(&disk, fsbtodb(&sblock, cgsblock(&sblock, cylno)), (void *)&osblock, SBLOCKSIZE) == -1) err(1, "bread: %s", disk.d_error); DBG_DUMP_FS(&osblock, dbg_line); } /* * Read the cylinder group and dump whatever was * requested. */ if (bread(&disk, fsbtodb(&sblock, cgtod(&sblock, cylno)), (void *)&acg, (size_t)sblock.fs_cgsize) == -1) err(1, "bread: %s", disk.d_error); if (cfg_lv & 0x008) DBG_DUMP_CG(&sblock, dbg_line, &acg); if (cfg_lv & 0x010) DBG_DUMP_INMAP(&sblock, dbg_line, &acg); if (cfg_lv & 0x020) DBG_DUMP_FRMAP(&sblock, dbg_line, &acg); if (cfg_lv & 0x040) { DBG_DUMP_CLMAP(&sblock, dbg_line, &acg); DBG_DUMP_CLSUM(&sblock, dbg_line, &acg); } #ifdef NOT_CURRENTLY /* * See the comment in sbin/growfs/debug.c for why this * is currently disabled, and what needs to be done to * re-enable it. */ if (disk.d_ufs == 1 && cfg_lv & 0x080) DBG_DUMP_SPTBL(&sblock, dbg_line, &acg); #endif } } if (cfg_lv & 0x300) { /* Dump the requested inode(s) */ if (cfg_in != -2) DUMP_WHOLE_INODE((ino_t)cfg_in, cfg_lv); else { for (in = cg_start * sblock.fs_ipg; in < (ino_t)cg_stop * sblock.fs_ipg; in++) DUMP_WHOLE_INODE(in, cfg_lv); } } DBG_CLOSE; DBG_LEAVE; return 0; } /* ********************************************** dump_whole_ufs1_inode ***** */ /* * Here we dump a list of all blocks allocated by this inode. We follow * all indirect blocks. */ void dump_whole_ufs1_inode(ino_t inode, int level) { DBG_FUNC("dump_whole_ufs1_inode") union dinodep dp; int rb; unsigned int ind2ctr, ind3ctr; ufs1_daddr_t *ind2ptr, *ind3ptr; char comment[80]; DBG_ENTER; /* * Read the inode from disk/cache. */ if (getinode(&disk, &dp, inode) == -1) err(1, "getinode: %s", disk.d_error); if (dp.dp1->di_nlink == 0) { DBG_LEAVE; return; /* inode not in use */ } /* * Dump the main inode structure. */ snprintf(comment, sizeof(comment), "Inode 0x%08jx", (uintmax_t)inode); if (level & 0x100) { DBG_DUMP_INO(&sblock, comment, dp.dp1); } if (!(level & 0x200)) { DBG_LEAVE; return; } /* * Ok, now prepare for dumping all direct and indirect pointers. */ rb = howmany(dp.dp1->di_size, sblock.fs_bsize) - UFS_NDADDR; if (rb > 0) { /* * Dump single indirect block. */ if (bread(&disk, fsbtodb(&sblock, dp.dp1->di_ib[0]), (void *)&i1blk, (size_t)sblock.fs_bsize) == -1) { err(1, "bread: %s", disk.d_error); } snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 0", (uintmax_t)inode); DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb); rb -= howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t)); } if (rb > 0) { /* * Dump double indirect blocks. */ if (bread(&disk, fsbtodb(&sblock, dp.dp1->di_ib[1]), (void *)&i2blk, (size_t)sblock.fs_bsize) == -1) { err(1, "bread: %s", disk.d_error); } snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 1", (uintmax_t)inode); DBG_DUMP_IBLK(&sblock, comment, i2blk, howmany(rb, howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t)))); for (ind2ctr = 0; ((ind2ctr < howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t))) && (rb > 0)); ind2ctr++) { ind2ptr = &((ufs1_daddr_t *)(void *)&i2blk)[ind2ctr]; if (bread(&disk, fsbtodb(&sblock, *ind2ptr), (void *)&i1blk, (size_t)sblock.fs_bsize) == -1) { err(1, "bread: %s", disk.d_error); } snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 1->%d", (uintmax_t)inode, ind2ctr); DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb); rb -= howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t)); } } if (rb > 0) { /* * Dump triple indirect blocks. */ if (bread(&disk, fsbtodb(&sblock, dp.dp1->di_ib[2]), (void *)&i3blk, (size_t)sblock.fs_bsize) == -1) { err(1, "bread: %s", disk.d_error); } snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 2", (uintmax_t)inode); #define SQUARE(a) ((a)*(a)) DBG_DUMP_IBLK(&sblock, comment, i3blk, howmany(rb, SQUARE(howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t))))); #undef SQUARE for (ind3ctr = 0; ((ind3ctr < howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t))) && (rb > 0)); ind3ctr++) { ind3ptr = &((ufs1_daddr_t *)(void *)&i3blk)[ind3ctr]; if (bread(&disk, fsbtodb(&sblock, *ind3ptr), (void *)&i2blk, (size_t)sblock.fs_bsize) == -1) { err(1, "bread: %s", disk.d_error); } snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 2->%d", (uintmax_t)inode, ind3ctr); DBG_DUMP_IBLK(&sblock, comment, i2blk, howmany(rb, howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t)))); for (ind2ctr = 0; ((ind2ctr < howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t))) && (rb > 0)); ind2ctr++) { ind2ptr=&((ufs1_daddr_t *)(void *)&i2blk) [ind2ctr]; if (bread(&disk, fsbtodb(&sblock, *ind2ptr), (void *)&i1blk, (size_t)sblock.fs_bsize) == -1) { err(1, "bread: %s", disk.d_error); } snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 2->%d->%d", (uintmax_t)inode, ind3ctr, ind3ctr); DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb); rb -= howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t)); } } } DBG_LEAVE; return; } /* ********************************************** dump_whole_ufs2_inode ***** */ /* * Here we dump a list of all blocks allocated by this inode. We follow * all indirect blocks. */ void dump_whole_ufs2_inode(ino_t inode, int level) { DBG_FUNC("dump_whole_ufs2_inode") union dinodep dp; int rb; unsigned int ind2ctr, ind3ctr; ufs2_daddr_t *ind2ptr, *ind3ptr; char comment[80]; DBG_ENTER; /* * Read the inode from disk/cache. */ if (getinode(&disk, &dp, inode) == -1) err(1, "getinode: %s", disk.d_error); if (dp.dp2->di_nlink == 0) { DBG_LEAVE; return; /* inode not in use */ } /* * Dump the main inode structure. */ snprintf(comment, sizeof(comment), "Inode 0x%08jx", (uintmax_t)inode); if (level & 0x100) { DBG_DUMP_INO(&sblock, comment, dp.dp2); } if (!(level & 0x200)) { DBG_LEAVE; return; } /* * Ok, now prepare for dumping all direct and indirect pointers. */ rb = howmany(dp.dp2->di_size, sblock.fs_bsize) - UFS_NDADDR; if (rb > 0) { /* * Dump single indirect block. */ if (bread(&disk, fsbtodb(&sblock, dp.dp2->di_ib[0]), (void *)&i1blk, (size_t)sblock.fs_bsize) == -1) { err(1, "bread: %s", disk.d_error); } snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 0", (uintmax_t)inode); DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb); rb -= howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t)); } if (rb > 0) { /* * Dump double indirect blocks. */ if (bread(&disk, fsbtodb(&sblock, dp.dp2->di_ib[1]), (void *)&i2blk, (size_t)sblock.fs_bsize) == -1) { err(1, "bread: %s", disk.d_error); } snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 1", (uintmax_t)inode); DBG_DUMP_IBLK(&sblock, comment, i2blk, howmany(rb, howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t)))); for (ind2ctr = 0; ((ind2ctr < howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t))) && (rb>0)); ind2ctr++) { ind2ptr = &((ufs2_daddr_t *)(void *)&i2blk)[ind2ctr]; if (bread(&disk, fsbtodb(&sblock, *ind2ptr), (void *)&i1blk, (size_t)sblock.fs_bsize) == -1) { err(1, "bread: %s", disk.d_error); } snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 1->%d", (uintmax_t)inode, ind2ctr); DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb); rb -= howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t)); } } if (rb > 0) { /* * Dump triple indirect blocks. */ if (bread(&disk, fsbtodb(&sblock, dp.dp2->di_ib[2]), (void *)&i3blk, (size_t)sblock.fs_bsize) == -1) { err(1, "bread: %s", disk.d_error); } snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 2", (uintmax_t)inode); #define SQUARE(a) ((a)*(a)) DBG_DUMP_IBLK(&sblock, comment, i3blk, howmany(rb, SQUARE(howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t))))); #undef SQUARE for (ind3ctr = 0; ((ind3ctr < howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t))) && (rb > 0)); ind3ctr++) { ind3ptr = &((ufs2_daddr_t *)(void *)&i3blk)[ind3ctr]; if (bread(&disk, fsbtodb(&sblock, *ind3ptr), (void *)&i2blk, (size_t)sblock.fs_bsize) == -1) { err(1, "bread: %s", disk.d_error); } snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 2->%d", (uintmax_t)inode, ind3ctr); DBG_DUMP_IBLK(&sblock, comment, i2blk, howmany(rb, howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t)))); for (ind2ctr = 0; ((ind2ctr < howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t))) && (rb > 0)); ind2ctr++) { ind2ptr = &((ufs2_daddr_t *)(void *)&i2blk) [ind2ctr]; if (bread(&disk, fsbtodb(&sblock, *ind2ptr), (void *)&i1blk, (size_t)sblock.fs_bsize) == -1) { err(1, "bread: %s", disk.d_error); } snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 2->%d->%d", (uintmax_t)inode, ind3ctr, ind3ctr); DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb); rb -= howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t)); } } } DBG_LEAVE; return; } /* ************************************************************* usage ***** */ /* * Dump a line of usage. */ void usage(void) { DBG_FUNC("usage") DBG_ENTER; fprintf(stderr, "usage: ffsinfo [-g cylinder_group] [-i inode] [-l level] " "[-o outfile]\n" " special | file\n"); DBG_LEAVE; exit(1); } diff --git a/sbin/fsck_msdosfs/boot.c b/sbin/fsck_msdosfs/boot.c index 3d1657ad66f3..f91609470ad7 100644 --- a/sbin/fsck_msdosfs/boot.c +++ b/sbin/fsck_msdosfs/boot.c @@ -1,376 +1,374 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 1995, 1997 Wolfgang Solfrank * Copyright (c) 1995 Martin Husemann * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #ifndef lint __RCSID("$NetBSD: boot.c,v 1.22 2020/01/11 16:29:07 christos Exp $"); -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include "ext.h" #include "fsutil.h" int readboot(int dosfs, struct bootblock *boot) { u_char block[DOSBOOTBLOCKSIZE]; u_char fsinfo[2 * DOSBOOTBLOCKSIZE]; int ret = FSOK; if ((size_t)read(dosfs, block, sizeof block) != sizeof block) { perr("could not read boot block"); return FSFATAL; } if (block[510] != 0x55 || block[511] != 0xaa) { pfatal("Invalid signature in boot block: %02x%02x", block[511], block[510]); return FSFATAL; } memset(boot, 0, sizeof *boot); boot->ValidFat = -1; /* Decode BIOS Parameter Block */ /* Bytes per sector: can only be 512, 1024, 2048 and 4096. */ boot->bpbBytesPerSec = block[11] + (block[12] << 8); if (boot->bpbBytesPerSec < DOSBOOTBLOCKSIZE_REAL || boot->bpbBytesPerSec > DOSBOOTBLOCKSIZE || !powerof2(boot->bpbBytesPerSec)) { pfatal("Invalid sector size: %u", boot->bpbBytesPerSec); return FSFATAL; } /* Sectors per cluster: can only be: 1, 2, 4, 8, 16, 32, 64, 128. */ boot->bpbSecPerClust = block[13]; if (boot->bpbSecPerClust == 0 || !powerof2(boot->bpbSecPerClust)) { pfatal("Invalid cluster size: %u", boot->bpbSecPerClust); return FSFATAL; } /* Reserved sectors: must be non-zero */ boot->bpbResSectors = block[14] + (block[15] << 8); if (boot->bpbResSectors < 1) { pfatal("Invalid reserved sectors: %u", boot->bpbResSectors); return FSFATAL; } /* Number of FATs */ boot->bpbFATs = block[16]; if (boot->bpbFATs == 0) { pfatal("Invalid number of FATs: %u", boot->bpbFATs); return FSFATAL; } /* Root directory entries for FAT12 and FAT16 */ boot->bpbRootDirEnts = block[17] + (block[18] << 8); if (!boot->bpbRootDirEnts) { /* bpbRootDirEnts = 0 suggests that we are FAT32 */ boot->flags |= FAT32; } /* Total sectors (16 bits) */ boot->bpbSectors = block[19] + (block[20] << 8); if (boot->bpbSectors != 0 && (boot->flags & FAT32)) { pfatal("Invalid 16-bit total sector count on FAT32: %u", boot->bpbSectors); return FSFATAL; } /* Media type: ignored */ boot->bpbMedia = block[21]; /* FAT12/FAT16: 16-bit count of sectors per FAT */ boot->bpbFATsmall = block[22] + (block[23] << 8); if (boot->bpbFATsmall != 0 && (boot->flags & FAT32)) { pfatal("Invalid 16-bit FAT sector count on FAT32: %u", boot->bpbFATsmall); return FSFATAL; } /* Legacy CHS geometry numbers: ignored */ boot->SecPerTrack = block[24] + (block[25] << 8); boot->bpbHeads = block[26] + (block[27] << 8); /* Hidden sectors: ignored */ boot->bpbHiddenSecs = block[28] + (block[29] << 8) + (block[30] << 16) + (block[31] << 24); /* Total sectors (32 bits) */ boot->bpbHugeSectors = block[32] + (block[33] << 8) + (block[34] << 16) + (block[35] << 24); if (boot->bpbHugeSectors == 0) { if (boot->flags & FAT32) { pfatal("FAT32 with sector count of zero"); return FSFATAL; } else if (boot->bpbSectors == 0) { pfatal("FAT with sector count of zero"); return FSFATAL; } boot->NumSectors = boot->bpbSectors; } else { if (boot->bpbSectors != 0) { pfatal("Invalid FAT sector count"); return FSFATAL; } boot->NumSectors = boot->bpbHugeSectors; } if (boot->flags & FAT32) { /* If the OEM Name field is EXFAT, it's not FAT32, so bail */ if (!memcmp(&block[3], "EXFAT ", 8)) { pfatal("exFAT filesystem is not supported."); return FSFATAL; } /* 32-bit count of sectors per FAT */ boot->FATsecs = block[36] + (block[37] << 8) + (block[38] << 16) + (block[39] << 24); if (block[40] & 0x80) boot->ValidFat = block[40] & 0x0f; /* FAT32 version, bail out if not 0.0 */ if (block[42] || block[43]) { pfatal("Unknown file system version: %x.%x", block[43], block[42]); return FSFATAL; } /* * Cluster number of the first cluster of root directory. * * Should be 2 but do not require it. */ boot->bpbRootClust = block[44] + (block[45] << 8) + (block[46] << 16) + (block[47] << 24); /* Sector number of the FSInfo structure, usually 1 */ boot->bpbFSInfo = block[48] + (block[49] << 8); /* Sector number of the backup boot block, ignored */ boot->bpbBackup = block[50] + (block[51] << 8); /* Check basic parameters */ if (boot->bpbFSInfo == 0) { /* * Either the BIOS Parameter Block has been corrupted, * or this is not a FAT32 filesystem, most likely an * exFAT filesystem. */ pfatal("Invalid FAT32 Extended BIOS Parameter Block"); return FSFATAL; } /* Read in and verify the FSInfo block */ if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec, SEEK_SET) != boot->bpbFSInfo * boot->bpbBytesPerSec || read(dosfs, fsinfo, sizeof fsinfo) != sizeof fsinfo) { perr("could not read fsinfo block"); return FSFATAL; } if (memcmp(fsinfo, "RRaA", 4) || memcmp(fsinfo + 0x1e4, "rrAa", 4) || fsinfo[0x1fc] || fsinfo[0x1fd] || fsinfo[0x1fe] != 0x55 || fsinfo[0x1ff] != 0xaa || fsinfo[0x3fc] || fsinfo[0x3fd] || fsinfo[0x3fe] != 0x55 || fsinfo[0x3ff] != 0xaa) { pwarn("Invalid signature in fsinfo block\n"); if (ask(0, "Fix")) { memcpy(fsinfo, "RRaA", 4); memcpy(fsinfo + 0x1e4, "rrAa", 4); fsinfo[0x1fc] = fsinfo[0x1fd] = 0; fsinfo[0x1fe] = 0x55; fsinfo[0x1ff] = 0xaa; fsinfo[0x3fc] = fsinfo[0x3fd] = 0; fsinfo[0x3fe] = 0x55; fsinfo[0x3ff] = 0xaa; if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec, SEEK_SET) != boot->bpbFSInfo * boot->bpbBytesPerSec || write(dosfs, fsinfo, sizeof fsinfo) != sizeof fsinfo) { perr("Unable to write bpbFSInfo"); return FSFATAL; } ret = FSBOOTMOD; } else boot->bpbFSInfo = 0; } else { /* We appear to have a valid FSInfo block, decode */ boot->FSFree = fsinfo[0x1e8] + (fsinfo[0x1e9] << 8) + (fsinfo[0x1ea] << 16) + (fsinfo[0x1eb] << 24); boot->FSNext = fsinfo[0x1ec] + (fsinfo[0x1ed] << 8) + (fsinfo[0x1ee] << 16) + (fsinfo[0x1ef] << 24); } } else { /* !FAT32: FAT12/FAT16 */ boot->FATsecs = boot->bpbFATsmall; } if (boot->FATsecs < 1 || boot->FATsecs > UINT32_MAX / boot->bpbFATs) { pfatal("Invalid FATs(%u) with FATsecs(%zu)", boot->bpbFATs, (size_t)boot->FATsecs); return FSFATAL; } boot->FirstCluster = (boot->bpbRootDirEnts * 32 + boot->bpbBytesPerSec - 1) / boot->bpbBytesPerSec + boot->bpbResSectors + boot->bpbFATs * boot->FATsecs; if (boot->FirstCluster + boot->bpbSecPerClust > boot->NumSectors) { pfatal("Cluster offset too large (%u clusters)\n", boot->FirstCluster); return FSFATAL; } /* * The number of clusters is derived from available data sectors, * divided by sectors per cluster. */ boot->NumClusters = (boot->NumSectors - boot->FirstCluster) / boot->bpbSecPerClust; if (boot->flags & FAT32) { if (boot->NumClusters > (CLUST_RSRVD & CLUST32_MASK)) { pfatal("Filesystem too big (%u clusters) for FAT32 partition", boot->NumClusters); return FSFATAL; } if (boot->NumClusters < (CLUST_RSRVD & CLUST16_MASK)) { pfatal("Filesystem too small (%u clusters) for FAT32 partition", boot->NumClusters); return FSFATAL; } boot->ClustMask = CLUST32_MASK; if (boot->bpbRootClust < CLUST_FIRST || boot->bpbRootClust >= boot->NumClusters) { pfatal("Root directory starts with cluster out of range(%u)", boot->bpbRootClust); return FSFATAL; } } else if (boot->NumClusters < (CLUST_RSRVD&CLUST12_MASK)) { boot->ClustMask = CLUST12_MASK; } else if (boot->NumClusters < (CLUST_RSRVD&CLUST16_MASK)) { boot->ClustMask = CLUST16_MASK; } else { pfatal("Filesystem too big (%u clusters) for non-FAT32 partition", boot->NumClusters); return FSFATAL; } switch (boot->ClustMask) { case CLUST32_MASK: boot->NumFatEntries = (boot->FATsecs * boot->bpbBytesPerSec) / 4; break; case CLUST16_MASK: boot->NumFatEntries = (boot->FATsecs * boot->bpbBytesPerSec) / 2; break; default: boot->NumFatEntries = (boot->FATsecs * boot->bpbBytesPerSec * 2) / 3; break; } if (boot->NumFatEntries < boot->NumClusters) { pfatal("FAT size too small, %u entries won't fit into %u sectors\n", boot->NumClusters, boot->FATsecs); return FSFATAL; } /* * There are two reserved clusters. To avoid adding CLUST_FIRST every * time we perform boundary checks, we increment the NumClusters by 2, * which is CLUST_FIRST to denote the first out-of-range cluster number. */ boot->NumClusters += CLUST_FIRST; boot->ClusterSize = boot->bpbBytesPerSec * boot->bpbSecPerClust; boot->NumFiles = 1; boot->NumFree = 0; return ret; } int writefsinfo(int dosfs, struct bootblock *boot) { u_char fsinfo[2 * DOSBOOTBLOCKSIZE]; if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec, SEEK_SET) != boot->bpbFSInfo * boot->bpbBytesPerSec || read(dosfs, fsinfo, sizeof fsinfo) != sizeof fsinfo) { perr("could not read fsinfo block"); return FSFATAL; } fsinfo[0x1e8] = (u_char)boot->FSFree; fsinfo[0x1e9] = (u_char)(boot->FSFree >> 8); fsinfo[0x1ea] = (u_char)(boot->FSFree >> 16); fsinfo[0x1eb] = (u_char)(boot->FSFree >> 24); fsinfo[0x1ec] = (u_char)boot->FSNext; fsinfo[0x1ed] = (u_char)(boot->FSNext >> 8); fsinfo[0x1ee] = (u_char)(boot->FSNext >> 16); fsinfo[0x1ef] = (u_char)(boot->FSNext >> 24); if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec, SEEK_SET) != boot->bpbFSInfo * boot->bpbBytesPerSec || write(dosfs, fsinfo, sizeof fsinfo) != sizeof fsinfo) { perr("Unable to write bpbFSInfo"); return FSFATAL; } /* * Technically, we should return FSBOOTMOD here. * * However, since Win95 OSR2 (the first M$ OS that has * support for FAT32) doesn't maintain the FSINFO block * correctly, it has to be fixed pretty often. * * Therefore, we handle the FSINFO block only informally, * fixing it if necessary, but otherwise ignoring the * fact that it was incorrect. */ return 0; } diff --git a/sbin/fsck_msdosfs/check.c b/sbin/fsck_msdosfs/check.c index 654ceeb9c5ca..f672a2ac515c 100644 --- a/sbin/fsck_msdosfs/check.c +++ b/sbin/fsck_msdosfs/check.c @@ -1,193 +1,191 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank * Copyright (c) 1995 Martin Husemann * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #ifndef lint __RCSID("$NetBSD: check.c,v 1.14 2006/06/05 16:51:18 christos Exp $"); -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #ifdef HAVE_LIBUTIL_H #include #endif #include #include #include #include #include #include "ext.h" #include "fsutil.h" int checkfilesys(const char *fname) { int dosfs; struct bootblock boot; struct fat_descriptor *fat = NULL; int finish_dosdirsection=0; int mod = 0; int ret = 8; int64_t freebytes; int64_t badbytes; rdonly = alwaysno; if (!preen) printf("** %s", fname); dosfs = open(fname, rdonly ? O_RDONLY : O_RDWR, 0); if (dosfs < 0 && !rdonly) { dosfs = open(fname, O_RDONLY, 0); if (dosfs >= 0) pwarn(" (NO WRITE)\n"); else if (!preen) printf("\n"); rdonly = 1; } else if (!preen) printf("\n"); if (dosfs < 0) { perr("Can't open `%s'", fname); printf("\n"); return 8; } if (readboot(dosfs, &boot) == FSFATAL) { close(dosfs); printf("\n"); return 8; } if (skipclean && preen && checkdirty(dosfs, &boot)) { printf("%s: ", fname); printf("FILESYSTEM CLEAN; SKIPPING CHECKS\n"); ret = 0; goto out; } if (!preen) { printf("** Phase 1 - Read FAT and checking connectivity\n"); } mod |= readfat(dosfs, &boot, &fat); if (mod & FSFATAL) { close(dosfs); return 8; } if (!preen) printf("** Phase 2 - Checking Directories\n"); mod |= resetDosDirSection(fat); finish_dosdirsection = 1; if (mod & FSFATAL) goto out; /* delay writing FATs */ mod |= handleDirTree(fat); if (mod & FSFATAL) goto out; if (!preen) printf("** Phase 3 - Checking for Lost Files\n"); mod |= checklost(fat); if (mod & FSFATAL) goto out; /* now write the FATs */ if (mod & FSFATMOD) { if (ask(1, "Update FATs")) { mod |= writefat(fat); if (mod & FSFATAL) goto out; } else mod |= FSERROR; } freebytes = (int64_t)boot.NumFree * boot.ClusterSize; badbytes = (int64_t)boot.NumBad * boot.ClusterSize; #ifdef HAVE_LIBUTIL_H char freestr[7], badstr[7]; humanize_number(freestr, sizeof(freestr), freebytes, "", HN_AUTOSCALE, HN_DECIMAL | HN_IEC_PREFIXES); if (boot.NumBad) { humanize_number(badstr, sizeof(badstr), badbytes, "", HN_AUTOSCALE, HN_B | HN_DECIMAL | HN_IEC_PREFIXES); pwarn("%d files, %sB free (%d clusters), %sB bad (%d clusters)\n", boot.NumFiles, freestr, boot.NumFree, badstr, boot.NumBad); } else { pwarn("%d files, %sB free (%d clusters)\n", boot.NumFiles, freestr, boot.NumFree); } #else if (boot.NumBad) pwarn("%d files, %jd KiB free (%d clusters), %jd KiB bad (%d clusters)\n", boot.NumFiles, (intmax_t)freebytes / 1024, boot.NumFree, (intmax_t)badbytes / 1024, boot.NumBad); else pwarn("%d files, %jd KiB free (%d clusters)\n", boot.NumFiles, (intmax_t)freebytes / 1024, boot.NumFree); #endif if (mod && (mod & FSERROR) == 0) { if (mod & FSDIRTY) { if (ask(1, "MARK FILE SYSTEM CLEAN") == 0) mod &= ~FSDIRTY; if (mod & FSDIRTY) { pwarn("MARKING FILE SYSTEM CLEAN\n"); mod |= cleardirty(fat); } else { pwarn("\n***** FILE SYSTEM IS LEFT MARKED AS DIRTY *****\n"); mod |= FSERROR; /* file system not clean */ } } } if (mod & (FSFATAL | FSERROR)) goto out; ret = 0; out: if (finish_dosdirsection) finishDosDirSection(); free(fat); close(dosfs); if (mod & (FSFATMOD|FSDIRMOD)) pwarn("\n***** FILE SYSTEM WAS MODIFIED *****\n"); return ret; } diff --git a/sbin/fsck_msdosfs/dir.c b/sbin/fsck_msdosfs/dir.c index 02fe07880e0e..c60eaab59b12 100644 --- a/sbin/fsck_msdosfs/dir.c +++ b/sbin/fsck_msdosfs/dir.c @@ -1,1181 +1,1179 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019 Google LLC * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank * Copyright (c) 1995 Martin Husemann * Some structure declaration borrowed from Paul Popelka * (paulp@uts.amdahl.com), see /sys/msdosfs/ for reference. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #ifndef lint __RCSID("$NetBSD: dir.c,v 1.20 2006/06/05 16:51:18 christos Exp $"); -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include "ext.h" #include "fsutil.h" #define SLOT_EMPTY 0x00 /* slot has never been used */ #define SLOT_E5 0x05 /* the real value is 0xe5 */ #define SLOT_DELETED 0xe5 /* file in this slot deleted */ #define ATTR_NORMAL 0x00 /* normal file */ #define ATTR_READONLY 0x01 /* file is readonly */ #define ATTR_HIDDEN 0x02 /* file is hidden */ #define ATTR_SYSTEM 0x04 /* file is a system file */ #define ATTR_VOLUME 0x08 /* entry is a volume label */ #define ATTR_DIRECTORY 0x10 /* entry is a directory name */ #define ATTR_ARCHIVE 0x20 /* file is new or modified */ #define ATTR_WIN95 0x0f /* long name record */ /* * This is the format of the contents of the deTime field in the direntry * structure. * We don't use bitfields because we don't know how compilers for * arbitrary machines will lay them out. */ #define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */ #define DT_2SECONDS_SHIFT 0 #define DT_MINUTES_MASK 0x7E0 /* minutes */ #define DT_MINUTES_SHIFT 5 #define DT_HOURS_MASK 0xF800 /* hours */ #define DT_HOURS_SHIFT 11 /* * This is the format of the contents of the deDate field in the direntry * structure. */ #define DD_DAY_MASK 0x1F /* day of month */ #define DD_DAY_SHIFT 0 #define DD_MONTH_MASK 0x1E0 /* month */ #define DD_MONTH_SHIFT 5 #define DD_YEAR_MASK 0xFE00 /* year - 1980 */ #define DD_YEAR_SHIFT 9 /* dir.c */ static struct dosDirEntry *newDosDirEntry(void); static void freeDosDirEntry(struct dosDirEntry *); static struct dirTodoNode *newDirTodo(void); static void freeDirTodo(struct dirTodoNode *); static char *fullpath(struct dosDirEntry *); static u_char calcShortSum(u_char *); static int delete(struct fat_descriptor *, cl_t, int, cl_t, int, int); static int removede(struct fat_descriptor *, u_char *, u_char *, cl_t, cl_t, cl_t, char *, int); static int checksize(struct fat_descriptor *, u_char *, struct dosDirEntry *); static int readDosDirSection(struct fat_descriptor *, struct dosDirEntry *); /* * Manage free dosDirEntry structures. */ static struct dosDirEntry *freede; static struct dosDirEntry * newDosDirEntry(void) { struct dosDirEntry *de; if (!(de = freede)) { if (!(de = malloc(sizeof *de))) return (NULL); } else freede = de->next; return de; } static void freeDosDirEntry(struct dosDirEntry *de) { de->next = freede; freede = de; } /* * The same for dirTodoNode structures. */ static struct dirTodoNode *freedt; static struct dirTodoNode * newDirTodo(void) { struct dirTodoNode *dt; if (!(dt = freedt)) { if (!(dt = malloc(sizeof *dt))) return 0; } else freedt = dt->next; return dt; } static void freeDirTodo(struct dirTodoNode *dt) { dt->next = freedt; freedt = dt; } /* * The stack of unread directories */ static struct dirTodoNode *pendingDirectories = NULL; /* * Return the full pathname for a directory entry. */ static char * fullpath(struct dosDirEntry *dir) { static char namebuf[MAXPATHLEN + 1]; char *cp, *np; int nl; cp = namebuf + sizeof namebuf; *--cp = '\0'; for(;;) { np = dir->lname[0] ? dir->lname : dir->name; nl = strlen(np); if (cp <= namebuf + 1 + nl) { *--cp = '?'; break; } cp -= nl; memcpy(cp, np, nl); dir = dir->parent; if (!dir) break; *--cp = '/'; } return cp; } /* * Calculate a checksum over an 8.3 alias name */ static inline u_char calcShortSum(u_char *p) { u_char sum = 0; int i; for (i = 0; i < 11; i++) { sum = (sum << 7)|(sum >> 1); /* rotate right */ sum += p[i]; } return sum; } /* * Global variables temporarily used during a directory scan */ static char longName[DOSLONGNAMELEN] = ""; static u_char *buffer = NULL; static u_char *delbuf = NULL; static struct dosDirEntry *rootDir; static struct dosDirEntry *lostDir; /* * Init internal state for a new directory scan. */ int resetDosDirSection(struct fat_descriptor *fat) { int rootdir_size, cluster_size; int ret = FSOK; size_t len; struct bootblock *boot; boot = fat_get_boot(fat); rootdir_size = boot->bpbRootDirEnts * 32; cluster_size = boot->bpbSecPerClust * boot->bpbBytesPerSec; if ((buffer = malloc(len = MAX(rootdir_size, cluster_size))) == NULL) { perr("No space for directory buffer (%zu)", len); return FSFATAL; } if ((delbuf = malloc(len = cluster_size)) == NULL) { free(buffer); perr("No space for directory delbuf (%zu)", len); return FSFATAL; } if ((rootDir = newDosDirEntry()) == NULL) { free(buffer); free(delbuf); perr("No space for directory entry"); return FSFATAL; } memset(rootDir, 0, sizeof *rootDir); if (boot->flags & FAT32) { if (!fat_is_cl_head(fat, boot->bpbRootClust)) { pfatal("Root directory doesn't start a cluster chain"); return FSFATAL; } rootDir->head = boot->bpbRootClust; } return ret; } /* * Cleanup after a directory scan */ void finishDosDirSection(void) { struct dirTodoNode *p, *np; struct dosDirEntry *d, *nd; for (p = pendingDirectories; p; p = np) { np = p->next; freeDirTodo(p); } pendingDirectories = NULL; for (d = rootDir; d; d = nd) { if ((nd = d->child) != NULL) { d->child = 0; continue; } if (!(nd = d->next)) nd = d->parent; freeDosDirEntry(d); } rootDir = lostDir = NULL; free(buffer); free(delbuf); buffer = NULL; delbuf = NULL; } /* * Delete directory entries between startcl, startoff and endcl, endoff. */ static int delete(struct fat_descriptor *fat, cl_t startcl, int startoff, cl_t endcl, int endoff, int notlast) { u_char *s, *e; off_t off; int clsz, fd; struct bootblock *boot; boot = fat_get_boot(fat); fd = fat_get_fd(fat); clsz = boot->bpbSecPerClust * boot->bpbBytesPerSec; s = delbuf + startoff; e = delbuf + clsz; while (fat_is_valid_cl(fat, startcl)) { if (startcl == endcl) { if (notlast) break; e = delbuf + endoff; } off = (startcl - CLUST_FIRST) * boot->bpbSecPerClust + boot->FirstCluster; off *= boot->bpbBytesPerSec; if (lseek(fd, off, SEEK_SET) != off) { perr("Unable to lseek to %" PRId64, off); return FSFATAL; } if (read(fd, delbuf, clsz) != clsz) { perr("Unable to read directory"); return FSFATAL; } while (s < e) { *s = SLOT_DELETED; s += 32; } if (lseek(fd, off, SEEK_SET) != off) { perr("Unable to lseek to %" PRId64, off); return FSFATAL; } if (write(fd, delbuf, clsz) != clsz) { perr("Unable to write directory"); return FSFATAL; } if (startcl == endcl) break; startcl = fat_get_cl_next(fat, startcl); s = delbuf; } return FSOK; } static int removede(struct fat_descriptor *fat, u_char *start, u_char *end, cl_t startcl, cl_t endcl, cl_t curcl, char *path, int type) { switch (type) { case 0: pwarn("Invalid long filename entry for %s\n", path); break; case 1: pwarn("Invalid long filename entry at end of directory %s\n", path); break; case 2: pwarn("Invalid long filename entry for volume label\n"); break; } if (ask(0, "Remove")) { if (startcl != curcl) { if (delete(fat, startcl, start - buffer, endcl, end - buffer, endcl == curcl) == FSFATAL) return FSFATAL; start = buffer; } /* startcl is < CLUST_FIRST for !FAT32 root */ if ((endcl == curcl) || (startcl < CLUST_FIRST)) for (; start < end; start += 32) *start = SLOT_DELETED; return FSDIRMOD; } return FSERROR; } /* * Check an in-memory file entry */ static int checksize(struct fat_descriptor *fat, u_char *p, struct dosDirEntry *dir) { int ret = FSOK; size_t chainsize; u_int64_t physicalSize; struct bootblock *boot; boot = fat_get_boot(fat); /* * Check size on ordinary files */ if (dir->head == CLUST_FREE) { physicalSize = 0; } else { if (!fat_is_valid_cl(fat, dir->head) || !fat_is_cl_head(fat, dir->head)) { pwarn("Directory entry %s of size %u referencing invalid cluster %u\n", fullpath(dir), dir->size, dir->head); if (ask(1, "Truncate")) { p[28] = p[29] = p[30] = p[31] = 0; p[26] = p[27] = 0; if (boot->ClustMask == CLUST32_MASK) p[20] = p[21] = 0; dir->size = 0; dir->head = CLUST_FREE; return FSDIRMOD; } else { return FSERROR; } } ret = checkchain(fat, dir->head, &chainsize); /* * Upon return, chainsize would hold the chain length * that checkchain() was able to validate, but if the user * refused the proposed repair, it would be unsafe to * proceed with directory entry fix, so bail out in that * case. */ if (ret == FSERROR) { return (FSERROR); } /* * The maximum file size on FAT32 is 4GiB - 1, which * will occupy a cluster chain of exactly 4GiB in * size. On 32-bit platforms, since size_t is 32-bit, * it would wrap back to 0. */ physicalSize = (u_int64_t)chainsize * boot->ClusterSize; } if (physicalSize < dir->size) { pwarn("size of %s is %u, should at most be %ju\n", fullpath(dir), dir->size, (uintmax_t)physicalSize); if (ask(1, "Truncate")) { dir->size = physicalSize; p[28] = (u_char)physicalSize; p[29] = (u_char)(physicalSize >> 8); p[30] = (u_char)(physicalSize >> 16); p[31] = (u_char)(physicalSize >> 24); return FSDIRMOD; } else return FSERROR; } else if (physicalSize - dir->size >= boot->ClusterSize) { pwarn("%s has too many clusters allocated\n", fullpath(dir)); if (ask(1, "Drop superfluous clusters")) { cl_t cl; u_int32_t sz, len; for (cl = dir->head, len = sz = 0; (sz += boot->ClusterSize) < dir->size; len++) cl = fat_get_cl_next(fat, cl); clearchain(fat, fat_get_cl_next(fat, cl)); ret = fat_set_cl_next(fat, cl, CLUST_EOF); return (FSFATMOD | ret); } else return FSERROR; } return FSOK; } static const u_char dot_name[11] = ". "; static const u_char dotdot_name[11] = ".. "; /* * Basic sanity check if the subdirectory have good '.' and '..' entries, * and they are directory entries. Further sanity checks are performed * when we traverse into it. */ static int check_subdirectory(struct fat_descriptor *fat, struct dosDirEntry *dir) { u_char *buf, *cp; off_t off; cl_t cl; int retval = FSOK; int fd; struct bootblock *boot; boot = fat_get_boot(fat); fd = fat_get_fd(fat); cl = dir->head; if (dir->parent && !fat_is_valid_cl(fat, cl)) { return FSERROR; } if (!(boot->flags & FAT32) && !dir->parent) { off = boot->bpbResSectors + boot->bpbFATs * boot->FATsecs; } else { off = (cl - CLUST_FIRST) * boot->bpbSecPerClust + boot->FirstCluster; } /* * We only need to check the first two entries of the directory, * which is found in the first sector of the directory entry, * so read in only the first sector. */ buf = malloc(boot->bpbBytesPerSec); if (buf == NULL) { perr("No space for directory buffer (%u)", boot->bpbBytesPerSec); return FSFATAL; } off *= boot->bpbBytesPerSec; if (lseek(fd, off, SEEK_SET) != off || read(fd, buf, boot->bpbBytesPerSec) != (ssize_t)boot->bpbBytesPerSec) { perr("Unable to read directory"); free(buf); return FSFATAL; } /* * Both `.' and `..' must be present and be the first two entries * and be ATTR_DIRECTORY of a valid subdirectory. */ cp = buf; if (memcmp(cp, dot_name, sizeof(dot_name)) != 0 || (cp[11] & ATTR_DIRECTORY) != ATTR_DIRECTORY) { pwarn("%s: Incorrect `.' for %s.\n", __func__, dir->name); retval |= FSERROR; } cp += 32; if (memcmp(cp, dotdot_name, sizeof(dotdot_name)) != 0 || (cp[11] & ATTR_DIRECTORY) != ATTR_DIRECTORY) { pwarn("%s: Incorrect `..' for %s. \n", __func__, dir->name); retval |= FSERROR; } free(buf); return retval; } /* * Read a directory and * - resolve long name records * - enter file and directory records into the parent's list * - push directories onto the todo-stack */ static int readDosDirSection(struct fat_descriptor *fat, struct dosDirEntry *dir) { struct bootblock *boot; struct dosDirEntry dirent, *d; u_char *p, *vallfn, *invlfn, *empty; off_t off; int fd, i, j, k, iosize, entries; bool is_legacyroot; cl_t cl, valcl = ~0, invcl = ~0, empcl = ~0; char *t; u_int lidx = 0; int shortSum; int mod = FSOK; size_t dirclusters; #define THISMOD 0x8000 /* Only used within this routine */ boot = fat_get_boot(fat); fd = fat_get_fd(fat); cl = dir->head; if (dir->parent && (!fat_is_valid_cl(fat, cl))) { /* * Already handled somewhere else. */ return FSOK; } shortSum = -1; vallfn = invlfn = empty = NULL; /* * If we are checking the legacy root (for FAT12/FAT16), * we will operate on the whole directory; otherwise, we * will operate on one cluster at a time, and also take * this opportunity to examine the chain. * * Derive how many entries we are going to encounter from * the I/O size. */ is_legacyroot = (dir->parent == NULL && !(boot->flags & FAT32)); if (is_legacyroot) { iosize = boot->bpbRootDirEnts * 32; entries = boot->bpbRootDirEnts; } else { iosize = boot->bpbSecPerClust * boot->bpbBytesPerSec; entries = iosize / 32; mod |= checkchain(fat, dir->head, &dirclusters); } do { if (is_legacyroot) { /* * Special case for FAT12/FAT16 root -- read * in the whole root directory. */ off = boot->bpbResSectors + boot->bpbFATs * boot->FATsecs; } else { /* * Otherwise, read in a cluster of the * directory. */ off = (cl - CLUST_FIRST) * boot->bpbSecPerClust + boot->FirstCluster; } off *= boot->bpbBytesPerSec; if (lseek(fd, off, SEEK_SET) != off || read(fd, buffer, iosize) != iosize) { perr("Unable to read directory"); return FSFATAL; } for (p = buffer, i = 0; i < entries; i++, p += 32) { if (dir->fsckflags & DIREMPWARN) { *p = SLOT_EMPTY; continue; } if (*p == SLOT_EMPTY || *p == SLOT_DELETED) { if (*p == SLOT_EMPTY) { dir->fsckflags |= DIREMPTY; empty = p; empcl = cl; } continue; } if (dir->fsckflags & DIREMPTY) { if (!(dir->fsckflags & DIREMPWARN)) { pwarn("%s has entries after end of directory\n", fullpath(dir)); if (ask(1, "Extend")) { u_char *q; dir->fsckflags &= ~DIREMPTY; if (delete(fat, empcl, empty - buffer, cl, p - buffer, 1) == FSFATAL) return FSFATAL; q = ((empcl == cl) ? empty : buffer); assert(q != NULL); for (; q < p; q += 32) *q = SLOT_DELETED; mod |= THISMOD|FSDIRMOD; } else if (ask(0, "Truncate")) dir->fsckflags |= DIREMPWARN; } if (dir->fsckflags & DIREMPWARN) { *p = SLOT_DELETED; mod |= THISMOD|FSDIRMOD; continue; } else if (dir->fsckflags & DIREMPTY) mod |= FSERROR; empty = NULL; } if (p[11] == ATTR_WIN95) { if (*p & LRFIRST) { if (shortSum != -1) { if (!invlfn) { invlfn = vallfn; invcl = valcl; } } memset(longName, 0, sizeof longName); shortSum = p[13]; vallfn = p; valcl = cl; } else if (shortSum != p[13] || lidx != (*p & LRNOMASK)) { if (!invlfn) { invlfn = vallfn; invcl = valcl; } if (!invlfn) { invlfn = p; invcl = cl; } vallfn = NULL; } lidx = *p & LRNOMASK; if (lidx == 0) { pwarn("invalid long name\n"); if (!invlfn) { invlfn = vallfn; invcl = valcl; } vallfn = NULL; continue; } t = longName + --lidx * 13; for (k = 1; k < 11 && t < longName + sizeof(longName); k += 2) { if (!p[k] && !p[k + 1]) break; *t++ = p[k]; /* * Warn about those unusable chars in msdosfs here? XXX */ if (p[k + 1]) t[-1] = '?'; } if (k >= 11) for (k = 14; k < 26 && t < longName + sizeof(longName); k += 2) { if (!p[k] && !p[k + 1]) break; *t++ = p[k]; if (p[k + 1]) t[-1] = '?'; } if (k >= 26) for (k = 28; k < 32 && t < longName + sizeof(longName); k += 2) { if (!p[k] && !p[k + 1]) break; *t++ = p[k]; if (p[k + 1]) t[-1] = '?'; } if (t >= longName + sizeof(longName)) { pwarn("long filename too long\n"); if (!invlfn) { invlfn = vallfn; invcl = valcl; } vallfn = NULL; } if (p[26] | (p[27] << 8)) { pwarn("long filename record cluster start != 0\n"); if (!invlfn) { invlfn = vallfn; invcl = cl; } vallfn = NULL; } continue; /* long records don't carry further * information */ } /* * This is a standard msdosfs directory entry. */ memset(&dirent, 0, sizeof dirent); /* * it's a short name record, but we need to know * more, so get the flags first. */ dirent.flags = p[11]; /* * Translate from 850 to ISO here XXX */ for (j = 0; j < 8; j++) dirent.name[j] = p[j]; dirent.name[8] = '\0'; for (k = 7; k >= 0 && dirent.name[k] == ' '; k--) dirent.name[k] = '\0'; if (k < 0 || dirent.name[k] != '\0') k++; if (dirent.name[0] == SLOT_E5) dirent.name[0] = 0xe5; if (dirent.flags & ATTR_VOLUME) { if (vallfn || invlfn) { mod |= removede(fat, invlfn ? invlfn : vallfn, p, invlfn ? invcl : valcl, -1, 0, fullpath(dir), 2); vallfn = NULL; invlfn = NULL; } continue; } if (p[8] != ' ') dirent.name[k++] = '.'; for (j = 0; j < 3; j++) dirent.name[k++] = p[j+8]; dirent.name[k] = '\0'; for (k--; k >= 0 && dirent.name[k] == ' '; k--) dirent.name[k] = '\0'; if (vallfn && shortSum != calcShortSum(p)) { if (!invlfn) { invlfn = vallfn; invcl = valcl; } vallfn = NULL; } dirent.head = p[26] | (p[27] << 8); if (boot->ClustMask == CLUST32_MASK) dirent.head |= (p[20] << 16) | (p[21] << 24); dirent.size = p[28] | (p[29] << 8) | (p[30] << 16) | (p[31] << 24); if (vallfn) { strlcpy(dirent.lname, longName, sizeof(dirent.lname)); longName[0] = '\0'; shortSum = -1; } dirent.parent = dir; dirent.next = dir->child; if (invlfn) { mod |= k = removede(fat, invlfn, vallfn ? vallfn : p, invcl, vallfn ? valcl : cl, cl, fullpath(&dirent), 0); if (mod & FSFATAL) return FSFATAL; if (vallfn ? (valcl == cl && vallfn != buffer) : p != buffer) if (k & FSDIRMOD) mod |= THISMOD; } vallfn = NULL; /* not used any longer */ invlfn = NULL; /* * Check if the directory entry is sane. * * '.' and '..' are skipped, their sanity is * checked somewhere else. * * For everything else, check if we have a new, * valid cluster chain (beginning of a file or * directory that was never previously claimed * by another file) when it's a non-empty file * or a directory. The sanity of the cluster * chain is checked at a later time when we * traverse into the directory, or examine the * file's directory entry. * * The only possible fix is to delete the entry * if it's a directory; for file, we have to * truncate the size to 0. */ if (!(dirent.flags & ATTR_DIRECTORY) || (strcmp(dirent.name, ".") != 0 && strcmp(dirent.name, "..") != 0)) { if ((dirent.size != 0 || (dirent.flags & ATTR_DIRECTORY)) && ((!fat_is_valid_cl(fat, dirent.head) || !fat_is_cl_head(fat, dirent.head)))) { if (!fat_is_valid_cl(fat, dirent.head)) { pwarn("%s starts with cluster out of range(%u)\n", fullpath(&dirent), dirent.head); } else { pwarn("%s doesn't start a new cluster chain\n", fullpath(&dirent)); } if (dirent.flags & ATTR_DIRECTORY) { if (ask(0, "Remove")) { *p = SLOT_DELETED; mod |= THISMOD|FSDIRMOD; } else mod |= FSERROR; continue; } else { if (ask(1, "Truncate")) { p[28] = p[29] = p[30] = p[31] = 0; p[26] = p[27] = 0; if (boot->ClustMask == CLUST32_MASK) p[20] = p[21] = 0; dirent.size = 0; dirent.head = 0; mod |= THISMOD|FSDIRMOD; } else mod |= FSERROR; } } } if (dirent.flags & ATTR_DIRECTORY) { /* * gather more info for directories */ struct dirTodoNode *n; if (dirent.size) { pwarn("Directory %s has size != 0\n", fullpath(&dirent)); if (ask(1, "Correct")) { p[28] = p[29] = p[30] = p[31] = 0; dirent.size = 0; mod |= THISMOD|FSDIRMOD; } else mod |= FSERROR; } /* * handle `.' and `..' specially */ if (strcmp(dirent.name, ".") == 0) { if (dirent.head != dir->head) { pwarn("`.' entry in %s has incorrect start cluster\n", fullpath(dir)); if (ask(1, "Correct")) { dirent.head = dir->head; p[26] = (u_char)dirent.head; p[27] = (u_char)(dirent.head >> 8); if (boot->ClustMask == CLUST32_MASK) { p[20] = (u_char)(dirent.head >> 16); p[21] = (u_char)(dirent.head >> 24); } mod |= THISMOD|FSDIRMOD; } else mod |= FSERROR; } continue; } else if (strcmp(dirent.name, "..") == 0) { if (dir->parent) { /* XXX */ if (!dir->parent->parent) { if (dirent.head) { pwarn("`..' entry in %s has non-zero start cluster\n", fullpath(dir)); if (ask(1, "Correct")) { dirent.head = 0; p[26] = p[27] = 0; if (boot->ClustMask == CLUST32_MASK) p[20] = p[21] = 0; mod |= THISMOD|FSDIRMOD; } else mod |= FSERROR; } } else if (dirent.head != dir->parent->head) { pwarn("`..' entry in %s has incorrect start cluster\n", fullpath(dir)); if (ask(1, "Correct")) { dirent.head = dir->parent->head; p[26] = (u_char)dirent.head; p[27] = (u_char)(dirent.head >> 8); if (boot->ClustMask == CLUST32_MASK) { p[20] = (u_char)(dirent.head >> 16); p[21] = (u_char)(dirent.head >> 24); } mod |= THISMOD|FSDIRMOD; } else mod |= FSERROR; } } continue; } else { /* * Only one directory entry can point * to dir->head, it's '.'. */ if (dirent.head == dir->head) { pwarn("%s entry in %s has incorrect start cluster\n", dirent.name, fullpath(dir)); if (ask(1, "Remove")) { *p = SLOT_DELETED; mod |= THISMOD|FSDIRMOD; } else mod |= FSERROR; continue; } else if ((check_subdirectory(fat, &dirent) & FSERROR) == FSERROR) { /* * A subdirectory should have * a dot (.) entry and a dot-dot * (..) entry of ATTR_DIRECTORY, * we will inspect further when * traversing into it. */ if (ask(1, "Remove")) { *p = SLOT_DELETED; mod |= THISMOD|FSDIRMOD; } else mod |= FSERROR; continue; } } /* create directory tree node */ if (!(d = newDosDirEntry())) { perr("No space for directory"); return FSFATAL; } memcpy(d, &dirent, sizeof(struct dosDirEntry)); /* link it into the tree */ dir->child = d; /* Enter this directory into the todo list */ if (!(n = newDirTodo())) { perr("No space for todo list"); return FSFATAL; } n->next = pendingDirectories; n->dir = d; pendingDirectories = n; } else { mod |= k = checksize(fat, p, &dirent); if (k & FSDIRMOD) mod |= THISMOD; } boot->NumFiles++; } if (is_legacyroot) { /* * Don't bother to write back right now because * we may continue to make modification to the * non-FAT32 root directory below. */ break; } else if (mod & THISMOD) { if (lseek(fd, off, SEEK_SET) != off || write(fd, buffer, iosize) != iosize) { perr("Unable to write directory"); return FSFATAL; } mod &= ~THISMOD; } } while (fat_is_valid_cl(fat, (cl = fat_get_cl_next(fat, cl)))); if (invlfn || vallfn) mod |= removede(fat, invlfn ? invlfn : vallfn, p, invlfn ? invcl : valcl, -1, 0, fullpath(dir), 1); /* * The root directory of non-FAT32 filesystems is in a special * area and may have been modified above removede() without * being written out. */ if ((mod & FSDIRMOD) && is_legacyroot) { if (lseek(fd, off, SEEK_SET) != off || write(fd, buffer, iosize) != iosize) { perr("Unable to write directory"); return FSFATAL; } mod &= ~THISMOD; } return mod & ~THISMOD; } int handleDirTree(struct fat_descriptor *fat) { int mod; mod = readDosDirSection(fat, rootDir); if (mod & FSFATAL) return FSFATAL; /* * process the directory todo list */ while (pendingDirectories) { struct dosDirEntry *dir = pendingDirectories->dir; struct dirTodoNode *n = pendingDirectories->next; /* * remove TODO entry now, the list might change during * directory reads */ freeDirTodo(pendingDirectories); pendingDirectories = n; /* * handle subdirectory */ mod |= readDosDirSection(fat, dir); if (mod & FSFATAL) return FSFATAL; } return mod; } /* * Try to reconnect a FAT chain into dir */ static u_char *lfbuf; static cl_t lfcl; static off_t lfoff; int reconnect(struct fat_descriptor *fat, cl_t head, size_t length) { struct bootblock *boot = fat_get_boot(fat); struct dosDirEntry d; int len, dosfs; u_char *p; dosfs = fat_get_fd(fat); if (!ask(1, "Reconnect")) return FSERROR; if (!lostDir) { for (lostDir = rootDir->child; lostDir; lostDir = lostDir->next) { if (!strcmp(lostDir->name, LOSTDIR)) break; } if (!lostDir) { /* Create LOSTDIR? XXX */ pwarn("No %s directory\n", LOSTDIR); return FSERROR; } } if (!lfbuf) { lfbuf = malloc(boot->ClusterSize); if (!lfbuf) { perr("No space for buffer"); return FSFATAL; } p = NULL; } else p = lfbuf; while (1) { if (p) for (; p < lfbuf + boot->ClusterSize; p += 32) if (*p == SLOT_EMPTY || *p == SLOT_DELETED) break; if (p && p < lfbuf + boot->ClusterSize) break; lfcl = p ? fat_get_cl_next(fat, lfcl) : lostDir->head; if (lfcl < CLUST_FIRST || lfcl >= boot->NumClusters) { /* Extend LOSTDIR? XXX */ pwarn("No space in %s\n", LOSTDIR); lfcl = (lostDir->head < boot->NumClusters) ? lostDir->head : 0; return FSERROR; } lfoff = (lfcl - CLUST_FIRST) * boot->ClusterSize + boot->FirstCluster * boot->bpbBytesPerSec; if (lseek(dosfs, lfoff, SEEK_SET) != lfoff || (size_t)read(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) { perr("could not read LOST.DIR"); return FSFATAL; } p = lfbuf; } boot->NumFiles++; /* Ensure uniqueness of entry here! XXX */ memset(&d, 0, sizeof d); /* worst case -1 = 4294967295, 10 digits */ len = snprintf(d.name, sizeof(d.name), "%u", head); d.flags = 0; d.head = head; d.size = length * boot->ClusterSize; memcpy(p, d.name, len); memset(p + len, ' ', 11 - len); memset(p + 11, 0, 32 - 11); p[26] = (u_char)d.head; p[27] = (u_char)(d.head >> 8); if (boot->ClustMask == CLUST32_MASK) { p[20] = (u_char)(d.head >> 16); p[21] = (u_char)(d.head >> 24); } p[28] = (u_char)d.size; p[29] = (u_char)(d.size >> 8); p[30] = (u_char)(d.size >> 16); p[31] = (u_char)(d.size >> 24); if (lseek(dosfs, lfoff, SEEK_SET) != lfoff || (size_t)write(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) { perr("could not write LOST.DIR"); return FSFATAL; } return FSDIRMOD; } void finishlf(void) { if (lfbuf) free(lfbuf); lfbuf = NULL; } diff --git a/sbin/fsck_msdosfs/fat.c b/sbin/fsck_msdosfs/fat.c index e35e2f27d305..567bfcd428cb 100644 --- a/sbin/fsck_msdosfs/fat.c +++ b/sbin/fsck_msdosfs/fat.c @@ -1,1328 +1,1326 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019 Google LLC * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank * Copyright (c) 1995 Martin Husemann * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #ifndef lint __RCSID("$NetBSD: fat.c,v 1.18 2006/06/05 16:51:18 christos Exp $"); -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include "ext.h" #include "fsutil.h" static int _readfat(struct fat_descriptor *); static inline struct bootblock* boot_of_(struct fat_descriptor *); static inline int fd_of_(struct fat_descriptor *); static inline bool valid_cl(struct fat_descriptor *, cl_t); /* * Head bitmap for FAT scanning. * * FAT32 have up to 2^28 = 256M entries, and FAT16/12 have much less. * For each cluster, we use 1 bit to represent if it's a head cluster * (the first cluster of a cluster chain). * * Head bitmap * =========== * Initially, we set all bits to 1. In readfat(), we traverse the * whole FAT and mark each cluster identified as "next" cluster as * 0. After the scan, we have a bitmap with 1's to indicate the * corresponding cluster was a "head" cluster. * * We use head bitmap to identify lost chains: a head cluster that was * not being claimed by any file or directories is the head cluster of * a lost chain. * * Handle of lost chains * ===================== * At the end of scanning, we can easily find all lost chain's heads * by finding out the 1's in the head bitmap. */ typedef struct long_bitmap { unsigned long *map; size_t count; /* Total set bits in the map */ } long_bitmap_t; static inline void bitmap_clear(long_bitmap_t *lbp, cl_t cl) { cl_t i = cl / LONG_BIT; unsigned long clearmask = ~(1UL << (cl % LONG_BIT)); assert((lbp->map[i] & ~clearmask) != 0); lbp->map[i] &= clearmask; lbp->count--; } static inline bool bitmap_get(long_bitmap_t *lbp, cl_t cl) { cl_t i = cl / LONG_BIT; unsigned long usedbit = 1UL << (cl % LONG_BIT); return ((lbp->map[i] & usedbit) == usedbit); } static inline bool bitmap_none_in_range(long_bitmap_t *lbp, cl_t cl) { cl_t i = cl / LONG_BIT; return (lbp->map[i] == 0); } static inline size_t bitmap_count(long_bitmap_t *lbp) { return (lbp->count); } static int bitmap_ctor(long_bitmap_t *lbp, size_t bits, bool allone) { size_t bitmap_size = roundup2(bits, LONG_BIT) / (LONG_BIT / 8); free(lbp->map); lbp->map = calloc(1, bitmap_size); if (lbp->map == NULL) return FSFATAL; if (allone) { memset(lbp->map, 0xff, bitmap_size); lbp->count = bits; } else { lbp->count = 0; } return FSOK; } static void bitmap_dtor(long_bitmap_t *lbp) { free(lbp->map); lbp->map = NULL; } /* * FAT32 can be as big as 256MiB (2^26 entries * 4 bytes), when we * can not ask the kernel to manage the access, use a simple LRU * cache with chunk size of 128 KiB to manage it. */ struct fat32_cache_entry { TAILQ_ENTRY(fat32_cache_entry) entries; uint8_t *chunk; /* pointer to chunk */ off_t addr; /* offset */ bool dirty; /* dirty bit */ }; static const size_t fat32_cache_chunk_size = 131072; /* MAXPHYS */ static const size_t fat32_cache_size = 4194304; static const size_t fat32_cache_entries = 32; /* XXXgcc: cache_size / cache_chunk_size */ /* * FAT table descriptor, represents a FAT table that is already loaded * into memory. */ struct fat_descriptor { struct bootblock *boot; uint8_t *fatbuf; cl_t (*get)(struct fat_descriptor *, cl_t); int (*set)(struct fat_descriptor *, cl_t, cl_t); long_bitmap_t headbitmap; int fd; bool is_mmapped; bool use_cache; size_t fatsize; size_t fat32_cached_chunks; TAILQ_HEAD(cachehead, fat32_cache_entry) fat32_cache_head; struct fat32_cache_entry *fat32_cache_allentries; off_t fat32_offset; off_t fat32_lastaddr; }; void fat_clear_cl_head(struct fat_descriptor *fat, cl_t cl) { bitmap_clear(&fat->headbitmap, cl); } bool fat_is_cl_head(struct fat_descriptor *fat, cl_t cl) { return (bitmap_get(&fat->headbitmap, cl)); } static inline bool fat_is_cl_head_in_range(struct fat_descriptor *fat, cl_t cl) { return (!(bitmap_none_in_range(&fat->headbitmap, cl))); } static size_t fat_get_head_count(struct fat_descriptor *fat) { return (bitmap_count(&fat->headbitmap)); } /* * FAT12 accessors. * * FAT12s are sufficiently small, expect it to always fit in the RAM. */ static inline uint8_t * fat_get_fat12_ptr(struct fat_descriptor *fat, cl_t cl) { return (fat->fatbuf + ((cl + (cl >> 1)))); } static cl_t fat_get_fat12_next(struct fat_descriptor *fat, cl_t cl) { const uint8_t *p; cl_t retval; p = fat_get_fat12_ptr(fat, cl); retval = le16dec(p); /* Odd cluster: lower 4 bits belongs to the subsequent cluster */ if ((cl & 1) == 1) retval >>= 4; retval &= CLUST12_MASK; if (retval >= (CLUST_BAD & CLUST12_MASK)) retval |= ~CLUST12_MASK; return (retval); } static int fat_set_fat12_next(struct fat_descriptor *fat, cl_t cl, cl_t nextcl) { uint8_t *p; /* Truncate 'nextcl' value, if needed */ nextcl &= CLUST12_MASK; p = fat_get_fat12_ptr(fat, cl); /* * Read in the 4 bits from the subsequent (for even clusters) * or the preceding (for odd clusters) cluster and combine * it to the nextcl value for encoding */ if ((cl & 1) == 0) { nextcl |= ((p[1] & 0xf0) << 8); } else { nextcl <<= 4; nextcl |= (p[0] & 0x0f); } le16enc(p, (uint16_t)nextcl); return (0); } /* * FAT16 accessors. * * FAT16s are sufficiently small, expect it to always fit in the RAM. */ static inline uint8_t * fat_get_fat16_ptr(struct fat_descriptor *fat, cl_t cl) { return (fat->fatbuf + (cl << 1)); } static cl_t fat_get_fat16_next(struct fat_descriptor *fat, cl_t cl) { const uint8_t *p; cl_t retval; p = fat_get_fat16_ptr(fat, cl); retval = le16dec(p) & CLUST16_MASK; if (retval >= (CLUST_BAD & CLUST16_MASK)) retval |= ~CLUST16_MASK; return (retval); } static int fat_set_fat16_next(struct fat_descriptor *fat, cl_t cl, cl_t nextcl) { uint8_t *p; /* Truncate 'nextcl' value, if needed */ nextcl &= CLUST16_MASK; p = fat_get_fat16_ptr(fat, cl); le16enc(p, (uint16_t)nextcl); return (0); } /* * FAT32 accessors. */ static inline uint8_t * fat_get_fat32_ptr(struct fat_descriptor *fat, cl_t cl) { return (fat->fatbuf + (cl << 2)); } static cl_t fat_get_fat32_next(struct fat_descriptor *fat, cl_t cl) { const uint8_t *p; cl_t retval; p = fat_get_fat32_ptr(fat, cl); retval = le32dec(p) & CLUST32_MASK; if (retval >= (CLUST_BAD & CLUST32_MASK)) retval |= ~CLUST32_MASK; return (retval); } static int fat_set_fat32_next(struct fat_descriptor *fat, cl_t cl, cl_t nextcl) { uint8_t *p; /* Truncate 'nextcl' value, if needed */ nextcl &= CLUST32_MASK; p = fat_get_fat32_ptr(fat, cl); le32enc(p, (uint32_t)nextcl); return (0); } static inline size_t fat_get_iosize(struct fat_descriptor *fat, off_t address) { if (address == fat->fat32_lastaddr) { return (fat->fatsize & ((off_t)fat32_cache_chunk_size - 1)); } else { return (fat32_cache_chunk_size); } } static int fat_flush_fat32_cache_entry(struct fat_descriptor *fat, struct fat32_cache_entry *entry) { int fd; off_t fat_addr; size_t writesize; fd = fd_of_(fat); if (!entry->dirty) return (FSOK); writesize = fat_get_iosize(fat, entry->addr); fat_addr = fat->fat32_offset + entry->addr; if (lseek(fd, fat_addr, SEEK_SET) != fat_addr || (size_t)write(fd, entry->chunk, writesize) != writesize) { pfatal("Unable to write FAT"); return (FSFATAL); } entry->dirty = false; return (FSOK); } static struct fat32_cache_entry * fat_get_fat32_cache_entry(struct fat_descriptor *fat, off_t addr, bool writing) { int fd; struct fat32_cache_entry *entry, *first; off_t fat_addr; size_t rwsize; addr &= ~(fat32_cache_chunk_size - 1); first = TAILQ_FIRST(&fat->fat32_cache_head); /* * Cache hit: if we already have the chunk, move it to list head */ TAILQ_FOREACH(entry, &fat->fat32_cache_head, entries) { if (entry->addr == addr) { if (writing) { entry->dirty = true; } if (entry != first) { TAILQ_REMOVE(&fat->fat32_cache_head, entry, entries); TAILQ_INSERT_HEAD(&fat->fat32_cache_head, entry, entries); } return (entry); } } /* * Cache miss: detach the chunk at tail of list, overwrite with * the located chunk, and populate with data from disk. */ entry = TAILQ_LAST(&fat->fat32_cache_head, cachehead); TAILQ_REMOVE(&fat->fat32_cache_head, entry, entries); if (fat_flush_fat32_cache_entry(fat, entry) != FSOK) { return (NULL); } rwsize = fat_get_iosize(fat, addr); fat_addr = fat->fat32_offset + addr; entry->addr = addr; fd = fd_of_(fat); if (lseek(fd, fat_addr, SEEK_SET) != fat_addr || (size_t)read(fd, entry->chunk, rwsize) != rwsize) { pfatal("Unable to read FAT"); return (NULL); } if (writing) { entry->dirty = true; } TAILQ_INSERT_HEAD(&fat->fat32_cache_head, entry, entries); return (entry); } static inline uint8_t * fat_get_fat32_cached_ptr(struct fat_descriptor *fat, cl_t cl, bool writing) { off_t addr, off; struct fat32_cache_entry *entry; addr = cl << 2; entry = fat_get_fat32_cache_entry(fat, addr, writing); if (entry != NULL) { off = addr & (fat32_cache_chunk_size - 1); return (entry->chunk + off); } else { return (NULL); } } static cl_t fat_get_fat32_cached_next(struct fat_descriptor *fat, cl_t cl) { const uint8_t *p; cl_t retval; p = fat_get_fat32_cached_ptr(fat, cl, false); if (p != NULL) { retval = le32dec(p) & CLUST32_MASK; if (retval >= (CLUST_BAD & CLUST32_MASK)) retval |= ~CLUST32_MASK; } else { retval = CLUST_DEAD; } return (retval); } static int fat_set_fat32_cached_next(struct fat_descriptor *fat, cl_t cl, cl_t nextcl) { uint8_t *p; /* Truncate 'nextcl' value, if needed */ nextcl &= CLUST32_MASK; p = fat_get_fat32_cached_ptr(fat, cl, true); if (p != NULL) { le32enc(p, (uint32_t)nextcl); return FSOK; } else { return FSFATAL; } } cl_t fat_get_cl_next(struct fat_descriptor *fat, cl_t cl) { if (!valid_cl(fat, cl)) { pfatal("Invalid cluster: %ud", cl); return CLUST_DEAD; } return (fat->get(fat, cl)); } int fat_set_cl_next(struct fat_descriptor *fat, cl_t cl, cl_t nextcl) { if (rdonly) { pwarn(" (NO WRITE)\n"); return FSFATAL; } if (!valid_cl(fat, cl)) { pfatal("Invalid cluster: %ud", cl); return FSFATAL; } return (fat->set(fat, cl, nextcl)); } static inline struct bootblock* boot_of_(struct fat_descriptor *fat) { return (fat->boot); } struct bootblock* fat_get_boot(struct fat_descriptor *fat) { return (boot_of_(fat)); } static inline int fd_of_(struct fat_descriptor *fat) { return (fat->fd); } int fat_get_fd(struct fat_descriptor * fat) { return (fd_of_(fat)); } /* * Whether a cl is in valid data range. */ bool fat_is_valid_cl(struct fat_descriptor *fat, cl_t cl) { return (valid_cl(fat, cl)); } static inline bool valid_cl(struct fat_descriptor *fat, cl_t cl) { const struct bootblock *boot = boot_of_(fat); return (cl >= CLUST_FIRST && cl < boot->NumClusters); } /* * The first 2 FAT entries contain pseudo-cluster numbers with the following * layout: * * 31...... ........ ........ .......0 * rrrr1111 11111111 11111111 mmmmmmmm FAT32 entry 0 * rrrrsh11 11111111 11111111 11111xxx FAT32 entry 1 * * 11111111 mmmmmmmm FAT16 entry 0 * sh111111 11111xxx FAT16 entry 1 * * r = reserved * m = BPB media ID byte * s = clean flag (1 = dismounted; 0 = still mounted) * h = hard error flag (1 = ok; 0 = I/O error) * x = any value ok */ int checkdirty(int fs, struct bootblock *boot) { off_t off; u_char *buffer; int ret = 0; size_t len; if (boot->ClustMask != CLUST16_MASK && boot->ClustMask != CLUST32_MASK) return 0; off = boot->bpbResSectors; off *= boot->bpbBytesPerSec; buffer = malloc(len = boot->bpbBytesPerSec); if (buffer == NULL) { perr("No space for FAT sectors (%zu)", len); return 1; } if (lseek(fs, off, SEEK_SET) != off) { perr("Unable to read FAT"); goto err; } if ((size_t)read(fs, buffer, boot->bpbBytesPerSec) != boot->bpbBytesPerSec) { perr("Unable to read FAT"); goto err; } /* * If we don't understand the FAT, then the file system must be * assumed to be unclean. */ if (buffer[0] != boot->bpbMedia || buffer[1] != 0xff) goto err; if (boot->ClustMask == CLUST16_MASK) { if ((buffer[2] & 0xf8) != 0xf8 || (buffer[3] & 0x3f) != 0x3f) goto err; } else { if (buffer[2] != 0xff || (buffer[3] & 0x0f) != 0x0f || (buffer[4] & 0xf8) != 0xf8 || buffer[5] != 0xff || buffer[6] != 0xff || (buffer[7] & 0x03) != 0x03) goto err; } /* * Now check the actual clean flag (and the no-error flag). */ if (boot->ClustMask == CLUST16_MASK) { if ((buffer[3] & 0xc0) == 0xc0) ret = 1; } else { if ((buffer[7] & 0x0c) == 0x0c) ret = 1; } err: free(buffer); return ret; } int cleardirty(struct fat_descriptor *fat) { int fd, ret = FSERROR; struct bootblock *boot; u_char *buffer; size_t len; off_t off; boot = boot_of_(fat); fd = fd_of_(fat); if (boot->ClustMask != CLUST16_MASK && boot->ClustMask != CLUST32_MASK) return 0; off = boot->bpbResSectors; off *= boot->bpbBytesPerSec; buffer = malloc(len = boot->bpbBytesPerSec); if (buffer == NULL) { perr("No memory for FAT sectors (%zu)", len); return 1; } if ((size_t)pread(fd, buffer, len, off) != len) { perr("Unable to read FAT"); goto err; } if (boot->ClustMask == CLUST16_MASK) { buffer[3] |= 0x80; } else { buffer[7] |= 0x08; } if ((size_t)pwrite(fd, buffer, len, off) != len) { perr("Unable to write FAT"); goto err; } ret = FSOK; err: free(buffer); return ret; } /* * Read a FAT from disk. Returns 1 if successful, 0 otherwise. */ static int _readfat(struct fat_descriptor *fat) { int fd; size_t i; off_t off; size_t readsize; struct bootblock *boot; struct fat32_cache_entry *entry; boot = boot_of_(fat); fd = fd_of_(fat); fat->fatsize = boot->FATsecs * boot->bpbBytesPerSec; off = boot->bpbResSectors; off *= boot->bpbBytesPerSec; fat->is_mmapped = false; fat->use_cache = false; /* Attempt to mmap() first */ if (allow_mmap) { fat->fatbuf = mmap(NULL, fat->fatsize, PROT_READ | (rdonly ? 0 : PROT_WRITE), MAP_SHARED, fd_of_(fat), off); if (fat->fatbuf != MAP_FAILED) { fat->is_mmapped = true; return 1; } } /* * Unfortunately, we were unable to mmap(). * * Only use the cache manager when it's necessary, that is, * when the FAT is sufficiently large; in that case, only * read in the first 4 MiB of FAT into memory, and split the * buffer into chunks and insert to the LRU queue to populate * the cache with data. */ if (boot->ClustMask == CLUST32_MASK && fat->fatsize >= fat32_cache_size) { readsize = fat32_cache_size; fat->use_cache = true; fat->fat32_offset = boot->bpbResSectors * boot->bpbBytesPerSec; fat->fat32_lastaddr = fat->fatsize & ~(fat32_cache_chunk_size); } else { readsize = fat->fatsize; } fat->fatbuf = malloc(readsize); if (fat->fatbuf == NULL) { perr("No space for FAT (%zu)", readsize); return 0; } if (lseek(fd, off, SEEK_SET) != off) { perr("Unable to read FAT"); goto err; } if ((size_t)read(fd, fat->fatbuf, readsize) != readsize) { perr("Unable to read FAT"); goto err; } /* * When cache is used, split the buffer into chunks, and * connect the buffer into the cache. */ if (fat->use_cache) { TAILQ_INIT(&fat->fat32_cache_head); entry = calloc(fat32_cache_entries, sizeof(*entry)); if (entry == NULL) { perr("No space for FAT cache (%zu of %zu)", fat32_cache_entries, sizeof(entry)); goto err; } for (i = 0; i < fat32_cache_entries; i++) { entry[i].addr = fat32_cache_chunk_size * i; entry[i].chunk = &fat->fatbuf[entry[i].addr]; TAILQ_INSERT_TAIL(&fat->fat32_cache_head, &entry[i], entries); } fat->fat32_cache_allentries = entry; } return 1; err: free(fat->fatbuf); fat->fatbuf = NULL; return 0; } static void releasefat(struct fat_descriptor *fat) { if (fat->is_mmapped) { munmap(fat->fatbuf, fat->fatsize); } else { if (fat->use_cache) { free(fat->fat32_cache_allentries); fat->fat32_cache_allentries = NULL; } free(fat->fatbuf); } fat->fatbuf = NULL; bitmap_dtor(&fat->headbitmap); } /* * Read or map a FAT and populate head bitmap */ int readfat(int fs, struct bootblock *boot, struct fat_descriptor **fp) { struct fat_descriptor *fat; u_char *buffer, *p; cl_t cl, nextcl; int ret = FSOK; boot->NumFree = boot->NumBad = 0; fat = calloc(1, sizeof(struct fat_descriptor)); if (fat == NULL) { perr("No space for FAT descriptor"); return FSFATAL; } fat->fd = fs; fat->boot = boot; if (!_readfat(fat)) { free(fat); return FSFATAL; } buffer = fat->fatbuf; /* Populate accessors */ switch(boot->ClustMask) { case CLUST12_MASK: fat->get = fat_get_fat12_next; fat->set = fat_set_fat12_next; break; case CLUST16_MASK: fat->get = fat_get_fat16_next; fat->set = fat_set_fat16_next; break; case CLUST32_MASK: if (fat->is_mmapped || !fat->use_cache) { fat->get = fat_get_fat32_next; fat->set = fat_set_fat32_next; } else { fat->get = fat_get_fat32_cached_next; fat->set = fat_set_fat32_cached_next; } break; default: pfatal("Invalid ClustMask: %d", boot->ClustMask); releasefat(fat); free(fat); return FSFATAL; } if (bitmap_ctor(&fat->headbitmap, boot->NumClusters, true) != FSOK) { perr("No space for head bitmap for FAT clusters (%zu)", (size_t)boot->NumClusters); releasefat(fat); free(fat); return FSFATAL; } if (buffer[0] != boot->bpbMedia || buffer[1] != 0xff || buffer[2] != 0xff || (boot->ClustMask == CLUST16_MASK && buffer[3] != 0xff) || (boot->ClustMask == CLUST32_MASK && ((buffer[3]&0x0f) != 0x0f || buffer[4] != 0xff || buffer[5] != 0xff || buffer[6] != 0xff || (buffer[7]&0x0f) != 0x0f))) { /* Windows 95 OSR2 (and possibly any later) changes * the FAT signature to 0xXXffff7f for FAT16 and to * 0xXXffff0fffffff07 for FAT32 upon boot, to know that the * file system is dirty if it doesn't reboot cleanly. * Check this special condition before errorring out. */ if (buffer[0] == boot->bpbMedia && buffer[1] == 0xff && buffer[2] == 0xff && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f) || (boot->ClustMask == CLUST32_MASK && buffer[3] == 0x0f && buffer[4] == 0xff && buffer[5] == 0xff && buffer[6] == 0xff && buffer[7] == 0x07))) ret |= FSDIRTY; else { /* just some odd byte sequence in FAT */ switch (boot->ClustMask) { case CLUST32_MASK: pwarn("%s (%02x%02x%02x%02x%02x%02x%02x%02x)\n", "FAT starts with odd byte sequence", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7]); break; case CLUST16_MASK: pwarn("%s (%02x%02x%02x%02x)\n", "FAT starts with odd byte sequence", buffer[0], buffer[1], buffer[2], buffer[3]); break; default: pwarn("%s (%02x%02x%02x)\n", "FAT starts with odd byte sequence", buffer[0], buffer[1], buffer[2]); break; } if (ask(1, "Correct")) { ret |= FSFATMOD; p = buffer; *p++ = (u_char)boot->bpbMedia; *p++ = 0xff; *p++ = 0xff; switch (boot->ClustMask) { case CLUST16_MASK: *p++ = 0xff; break; case CLUST32_MASK: *p++ = 0x0f; *p++ = 0xff; *p++ = 0xff; *p++ = 0xff; *p++ = 0x0f; break; default: break; } } } } /* * Traverse the FAT table and populate head map. Initially, we * consider all clusters as possible head cluster (beginning of * a file or directory), and traverse the whole allocation table * by marking every non-head nodes as such (detailed below) and * fix obvious issues while we walk. * * For each "next" cluster, the possible values are: * * a) CLUST_FREE or CLUST_BAD. The *current* cluster can't be a * head node. * b) An out-of-range value. The only fix would be to truncate at * the cluster. * c) A valid cluster. It means that cluster (nextcl) is not a * head cluster. Note that during the scan, every cluster is * expected to be seen for at most once, and when we saw them * twice, it means a cross-linked chain which should be * truncated at the current cluster. * * After scan, the remaining set bits indicates all possible * head nodes, because they were never claimed by any other * node as the next node, but we do not know if these chains * would end with a valid EOF marker. We will check that in * checkchain() at a later time when checking directories, * where these head nodes would be marked as non-head. * * In the final pass, all head nodes should be cleared, and if * there is still head nodes, these would be leaders of lost * chain. */ for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) { nextcl = fat_get_cl_next(fat, cl); /* Check if the next cluster number is valid */ if (nextcl == CLUST_FREE) { /* Save a hint for next free cluster */ if (boot->FSNext == 0) { boot->FSNext = cl; } if (fat_is_cl_head(fat, cl)) { fat_clear_cl_head(fat, cl); } boot->NumFree++; } else if (nextcl == CLUST_BAD) { if (fat_is_cl_head(fat, cl)) { fat_clear_cl_head(fat, cl); } boot->NumBad++; } else if (!valid_cl(fat, nextcl) && nextcl < CLUST_RSRVD) { pwarn("Cluster %u continues with out of range " "cluster number %u\n", cl, nextcl & boot->ClustMask); if (ask(0, "Truncate")) { ret |= fat_set_cl_next(fat, cl, CLUST_EOF); ret |= FSFATMOD; } } else if (valid_cl(fat, nextcl)) { if (fat_is_cl_head(fat, nextcl)) { fat_clear_cl_head(fat, nextcl); } else { pwarn("Cluster %u crossed another chain at %u\n", cl, nextcl); if (ask(0, "Truncate")) { ret |= fat_set_cl_next(fat, cl, CLUST_EOF); ret |= FSFATMOD; } } } } if (ret & FSFATAL) { releasefat(fat); free(fat); *fp = NULL; } else *fp = fat; return ret; } /* * Get type of reserved cluster */ const char * rsrvdcltype(cl_t cl) { if (cl == CLUST_FREE) return "free"; if (cl < CLUST_BAD) return "reserved"; if (cl > CLUST_BAD) return "as EOF"; return "bad"; } /* * Examine a cluster chain for errors and count its size. */ int checkchain(struct fat_descriptor *fat, cl_t head, size_t *chainsize) { cl_t prev_cl, current_cl, next_cl; const char *op; /* * We expect that the caller to give us a real, unvisited 'head' * cluster, and it must be a valid cluster. While scanning the * FAT table, we already excluded all clusters that was claimed * as a "next" cluster. Assert all the three conditions. */ assert(valid_cl(fat, head)); assert(fat_is_cl_head(fat, head)); /* * Immediately mark the 'head' cluster that we are about to visit. */ fat_clear_cl_head(fat, head); /* * The allocation of a non-zero sized file or directory is * represented as a singly linked list, and the tail node * would be the EOF marker (>=CLUST_EOFS). * * With a valid head node at hand, we expect all subsequent * cluster to be either a not yet seen and valid cluster (we * would continue counting), or the EOF marker (we conclude * the scan of this chain). * * For all other cases, the chain is invalid, and the only * viable fix would be to truncate at the current node (mark * it as EOF) when the next node violates that. */ *chainsize = 0; prev_cl = current_cl = head; for (next_cl = fat_get_cl_next(fat, current_cl); valid_cl(fat, next_cl); prev_cl = current_cl, current_cl = next_cl, next_cl = fat_get_cl_next(fat, current_cl)) (*chainsize)++; /* A natural end */ if (next_cl >= CLUST_EOFS) { (*chainsize)++; return FSOK; } /* * The chain ended with an out-of-range cluster number. * * If the current node is e.g. CLUST_FREE, CLUST_BAD, etc., * it should not be present in a chain and we has to truncate * at the previous node. * * If the current cluster points to an invalid cluster, the * current cluster might have useful data and we truncate at * the current cluster instead. */ if (next_cl == CLUST_FREE || next_cl >= CLUST_RSRVD) { pwarn("Cluster chain starting at %u ends with cluster marked %s\n", head, rsrvdcltype(next_cl)); current_cl = prev_cl; } else { pwarn("Cluster chain starting at %u ends with cluster out of range (%u)\n", head, next_cl & boot_of_(fat)->ClustMask); (*chainsize)++; } if (*chainsize > 0) { op = "Truncate"; next_cl = CLUST_EOF; } else { op = "Clear"; next_cl = CLUST_FREE; } if (ask(0, "%s", op)) { return (fat_set_cl_next(fat, current_cl, next_cl) | FSFATMOD); } else { return (FSERROR); } } /* * Clear cluster chain from head. */ void clearchain(struct fat_descriptor *fat, cl_t head) { cl_t current_cl, next_cl; struct bootblock *boot = boot_of_(fat); current_cl = head; while (valid_cl(fat, current_cl)) { next_cl = fat_get_cl_next(fat, current_cl); (void)fat_set_cl_next(fat, current_cl, CLUST_FREE); boot->NumFree++; current_cl = next_cl; } } /* * Overwrite the n-th FAT with FAT0 */ static int copyfat(struct fat_descriptor *fat, int n) { size_t rwsize, tailsize, blobs, i; off_t dst_off, src_off; struct bootblock *boot; int ret, fd; ret = FSOK; fd = fd_of_(fat); boot = boot_of_(fat); blobs = howmany(fat->fatsize, fat32_cache_size); tailsize = fat->fatsize % fat32_cache_size; if (tailsize == 0) { tailsize = fat32_cache_size; } rwsize = fat32_cache_size; src_off = fat->fat32_offset; dst_off = boot->bpbResSectors + n * boot->FATsecs; dst_off *= boot->bpbBytesPerSec; for (i = 0; i < blobs; i++, src_off += fat32_cache_size, dst_off += fat32_cache_size) { if (i == blobs - 1) { rwsize = tailsize; } if ((lseek(fd, src_off, SEEK_SET) != src_off || (size_t)read(fd, fat->fatbuf, rwsize) != rwsize) && ret == FSOK) { perr("Unable to read FAT0"); ret = FSFATAL; continue; } if ((lseek(fd, dst_off, SEEK_SET) != dst_off || (size_t)write(fd, fat->fatbuf, rwsize) != rwsize) && ret == FSOK) { perr("Unable to write FAT %d", n); ret = FSERROR; } } return (ret); } /* * Write out FAT */ int writefat(struct fat_descriptor *fat) { u_int i; size_t writesz; off_t dst_base; int ret = FSOK, fd; struct bootblock *boot; struct fat32_cache_entry *entry; boot = boot_of_(fat); fd = fd_of_(fat); if (fat->use_cache) { /* * Attempt to flush all in-flight cache, and bail out * if we encountered an error (but only emit error * message once). Stop proceeding with copyfat() * if any flush failed. */ TAILQ_FOREACH(entry, &fat->fat32_cache_head, entries) { if (fat_flush_fat32_cache_entry(fat, entry) != FSOK) { if (ret == FSOK) { perr("Unable to write FAT"); ret = FSFATAL; } } } if (ret != FSOK) return (ret); /* Update backup copies of FAT, error is not fatal */ for (i = 1; i < boot->bpbFATs; i++) { if (copyfat(fat, i) != FSOK) ret = FSERROR; } } else { writesz = fat->fatsize; for (i = fat->is_mmapped ? 1 : 0; i < boot->bpbFATs; i++) { dst_base = boot->bpbResSectors + i * boot->FATsecs; dst_base *= boot->bpbBytesPerSec; if ((lseek(fd, dst_base, SEEK_SET) != dst_base || (size_t)write(fd, fat->fatbuf, writesz) != writesz) && ret == FSOK) { perr("Unable to write FAT %d", i); ret = ((i == 0) ? FSFATAL : FSERROR); } } } return ret; } /* * Check a complete in-memory FAT for lost cluster chains */ int checklost(struct fat_descriptor *fat) { cl_t head; int mod = FSOK; int dosfs, ret; size_t chains, chainlength; struct bootblock *boot; dosfs = fd_of_(fat); boot = boot_of_(fat); /* * At this point, we have already traversed all directories. * All remaining chain heads in the bitmap are heads of lost * chains. */ chains = fat_get_head_count(fat); for (head = CLUST_FIRST; chains > 0 && head < boot->NumClusters; ) { /* * We expect the bitmap to be very sparse, so skip if * the range is full of 0's */ if (head % LONG_BIT == 0 && !fat_is_cl_head_in_range(fat, head)) { head += LONG_BIT; continue; } if (fat_is_cl_head(fat, head)) { ret = checkchain(fat, head, &chainlength); if (ret != FSERROR && chainlength > 0) { pwarn("Lost cluster chain at cluster %u\n" "%zd Cluster(s) lost\n", head, chainlength); mod |= ret = reconnect(fat, head, chainlength); } if (mod & FSFATAL) break; if (ret == FSERROR && ask(0, "Clear")) { clearchain(fat, head); mod |= FSFATMOD; } chains--; } head++; } finishlf(); if (boot->bpbFSInfo) { ret = 0; if (boot->FSFree != 0xffffffffU && boot->FSFree != boot->NumFree) { pwarn("Free space in FSInfo block (%u) not correct (%u)\n", boot->FSFree, boot->NumFree); if (ask(1, "Fix")) { boot->FSFree = boot->NumFree; ret = 1; } } if (boot->FSNext != 0xffffffffU && (boot->FSNext >= boot->NumClusters || (boot->NumFree && fat_get_cl_next(fat, boot->FSNext) != CLUST_FREE))) { pwarn("Next free cluster in FSInfo block (%u) %s\n", boot->FSNext, (boot->FSNext >= boot->NumClusters) ? "invalid" : "not free"); if (ask(1, "Fix")) for (head = CLUST_FIRST; head < boot->NumClusters; head++) if (fat_get_cl_next(fat, head) == CLUST_FREE) { boot->FSNext = head; ret = 1; break; } } if (ret) mod |= writefsinfo(dosfs, boot); } return mod; } diff --git a/sbin/fsck_msdosfs/main.c b/sbin/fsck_msdosfs/main.c index de54cd18eae7..0713189daa2d 100644 --- a/sbin/fsck_msdosfs/main.c +++ b/sbin/fsck_msdosfs/main.c @@ -1,163 +1,161 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 1995 Wolfgang Solfrank * Copyright (c) 1995 Martin Husemann * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #ifndef lint __RCSID("$NetBSD: main.c,v 1.10 1997/10/01 02:18:14 enami Exp $"); -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include "fsutil.h" #include "ext.h" int alwaysno; /* assume "no" for all questions */ int alwaysyes; /* assume "yes" for all questions */ int preen; /* set when preening */ int rdonly; /* device is opened read only (supersedes above) */ int skipclean; /* skip clean file systems if preening */ int allow_mmap; /* Allow the use of mmap(), if possible */ static void usage(void) __dead2; static void usage(void) { fprintf(stderr, "%s\n%s\n", "usage: fsck_msdosfs -p [-f] filesystem ...", " fsck_msdosfs [-ny] filesystem ..."); exit(1); } int main(int argc, char **argv) { int ret = 0, erg; int ch; skipclean = 1; allow_mmap = 1; while ((ch = getopt(argc, argv, "CfFnpyM")) != -1) { switch (ch) { case 'C': /* for fsck_ffs compatibility */ break; case 'f': skipclean = 0; break; case 'F': /* * We can never run in the background. We must exit * silently with a nonzero exit code so that fsck(8) * can probe our support for -F. The exit code * doesn't really matter, but we use an unusual one * in case someone tries -F directly. The -F flag * is intentionally left out of the usage message. */ exit(5); case 'n': alwaysno = 1; alwaysyes = 0; break; case 'y': alwaysyes = 1; alwaysno = 0; break; case 'p': preen = 1; break; case 'M': allow_mmap = 0; break; default: usage(); break; } } argc -= optind; argv += optind; if (!argc) usage(); while (--argc >= 0) { setcdevname(*argv, preen); erg = checkfilesys(*argv++); if (erg > ret) ret = erg; } return ret; } /*VARARGS*/ int ask(int def, const char *fmt, ...) { va_list ap; char prompt[256]; int c; if (alwaysyes || alwaysno || rdonly) def = (alwaysyes && !rdonly && !alwaysno); if (preen) { if (def) printf("FIXED\n"); return def; } va_start(ap, fmt); vsnprintf(prompt, sizeof(prompt), fmt, ap); va_end(ap); if (alwaysyes || alwaysno || rdonly) { printf("%s? %s\n", prompt, def ? "yes" : "no"); return def; } do { printf("%s? [yn] ", prompt); fflush(stdout); c = getchar(); while (c != '\n' && getchar() != '\n') if (feof(stdin)) return 0; } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N'); return c == 'y' || c == 'Y'; } diff --git a/sbin/fsdb/fsdb.c b/sbin/fsdb/fsdb.c index 2d8c75cce5fe..48526ad4044b 100644 --- a/sbin/fsdb/fsdb.c +++ b/sbin/fsdb/fsdb.c @@ -1,1288 +1,1283 @@ /* $NetBSD: fsdb.c,v 1.2 1995/10/08 23:18:10 thorpej Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1995 John T. Kohl * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fsdb.h" #include "fsck.h" static void usage(void) __dead2; int cmdloop(void); static int compare_blk32(uint32_t *wantedblk, uint32_t curblk); static int compare_blk64(uint64_t *wantedblk, uint64_t curblk); static int founddatablk(uint64_t blk); static int find_blks32(uint32_t *buf, int size, uint32_t *blknum); static int find_blks64(uint64_t *buf, int size, uint64_t *blknum); static int find_indirblks32(uint32_t blk, int ind_level, uint32_t *blknum); static int find_indirblks64(uint64_t blk, int ind_level, uint64_t *blknum); /* * Track modifications to the filesystem. Two types of changes are tracked. * The first type of changes are those that are not critical to the integrity * of the filesystem such as owner, group, time stamps, access mode, and * generation number. The second type of changes are those that do affect * the integrity of the filesystem including zeroing inodes, changing block * pointers, directory entries, link counts, file lengths, file types and * file flags. * * When quitting having made no changes or only changes to data that is not * critical to filesystem integrity, the clean state of the filesystem is * left unchanged. But if filesystem critical data are changed then fsdb * will set the unclean flag which will require a full fsck to be run * before the filesystem can be mounted. */ static int fsnoncritmodified; /* filesystem non-critical modifications */ static int fscritmodified; /* filesystem integrity critical mods */ struct inode curip; union dinode *curinode; ino_t curinum, ocurrent; static void usage(void) { fprintf(stderr, "usage: fsdb [-d] [-f] [-r] fsname\n"); exit(1); } /* * We suck in lots of fsck code, and just pick & choose the stuff we want. * * fsreadfd is set up to read from the file system, fswritefd to write to * the file system. */ int main(int argc, char *argv[]) { int ch, rval; char *fsys = NULL; while (-1 != (ch = getopt(argc, argv, "fdr"))) { switch (ch) { case 'f': /* The -f option is left for historical * reasons and has no meaning. */ break; case 'd': debug++; break; case 'r': nflag++; /* "no" in fsck, readonly for us */ break; default: usage(); } } argc -= optind; argv += optind; if (argc != 1) usage(); else fsys = argv[0]; sblock_init(); if (openfilesys(fsys) == 0 || readsb() == 0 || setup(fsys) == 0) errx(1, "cannot set up file system `%s'", fsys); if (fswritefd < 0) nflag++; printf("%s file system `%s'\nLast Mounted on %s\n", nflag? "Examining": "Editing", fsys, sblock.fs_fsmnt); rval = cmdloop(); if (!nflag) { if (fscritmodified != 0) { sblock.fs_clean = 0; /* mark it dirty */ sbdirty(); } ckfini(fscritmodified ? 0 : sblock.fs_clean); if (fscritmodified == 0) exit(0); printf("*** FILE SYSTEM MARKED DIRTY\n"); printf("*** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE\n"); printf("*** IF IT IS MOUNTED, RE-MOUNT WITH -u -o reload\n"); } exit(rval); } #define CMDFUNC(func) int func(int argc, char *argv[]) #define CMDFUNCSTART(func) int func(int argc, char *argv[]) CMDFUNC(helpfn); CMDFUNC(focus); /* focus on inode */ CMDFUNC(active); /* print active inode */ CMDFUNC(blocks); /* print blocks for active inode */ CMDFUNC(focusname); /* focus by name */ CMDFUNC(zapi); /* clear inode */ CMDFUNC(uplink); /* incr link */ CMDFUNC(downlink); /* decr link */ CMDFUNC(linkcount); /* set link count */ CMDFUNC(quit); /* quit */ CMDFUNC(quitclean); /* quit with filesystem marked clean */ CMDFUNC(findblk); /* find block */ CMDFUNC(ls); /* list directory */ CMDFUNC(rm); /* remove name */ CMDFUNC(ln); /* add name */ CMDFUNC(newtype); /* change type */ CMDFUNC(chmode); /* change mode */ CMDFUNC(chlen); /* change length */ CMDFUNC(chaflags); /* change flags */ CMDFUNC(chgen); /* change generation */ CMDFUNC(chowner); /* change owner */ CMDFUNC(chgroup); /* Change group */ CMDFUNC(back); /* pop back to last ino */ CMDFUNC(chbtime); /* Change btime */ CMDFUNC(chmtime); /* Change mtime */ CMDFUNC(chctime); /* Change ctime */ CMDFUNC(chatime); /* Change atime */ CMDFUNC(chinum); /* Change inode # of dirent */ CMDFUNC(chname); /* Change dirname of dirent */ CMDFUNC(chsize); /* Change size */ CMDFUNC(chdb); /* Change direct block pointer */ struct cmdtable cmds[] = { { "help", "Print out help", 1, 1, FL_RO, helpfn }, { "?", "Print out help", 1, 1, FL_RO, helpfn }, { "inode", "Set active inode to INUM", 2, 2, FL_RO, focus }, { "clri", "Clear inode INUM", 2, 2, FL_CWR, zapi }, { "lookup", "Set active inode by looking up NAME", 2, 2, FL_RO | FL_ST, focusname }, { "cd", "Set active inode by looking up NAME", 2, 2, FL_RO | FL_ST, focusname }, { "back", "Go to previous active inode", 1, 1, FL_RO, back }, { "active", "Print active inode", 1, 1, FL_RO, active }, { "print", "Print active inode", 1, 1, FL_RO, active }, { "blocks", "Print block numbers of active inode", 1, 1, FL_RO, blocks }, { "uplink", "Increment link count", 1, 1, FL_CWR, uplink }, { "downlink", "Decrement link count", 1, 1, FL_CWR, downlink }, { "linkcount", "Set link count to COUNT", 2, 2, FL_CWR, linkcount }, { "findblk", "Find inode owning disk block(s)", 2, 33, FL_RO, findblk}, { "ls", "List current inode as directory", 1, 1, FL_RO, ls }, { "rm", "Remove NAME from current inode directory", 2, 2, FL_CWR | FL_ST, rm }, { "del", "Remove NAME from current inode directory", 2, 2, FL_CWR | FL_ST, rm }, { "ln", "Hardlink INO into current inode directory as NAME", 3, 3, FL_CWR | FL_ST, ln }, { "chinum", "Change dir entry number INDEX to INUM", 3, 3, FL_CWR, chinum }, { "chname", "Change dir entry number INDEX to NAME", 3, 3, FL_WR | FL_ST, chname }, { "chtype", "Change type of current inode to TYPE", 2, 2, FL_CWR, newtype }, { "chmod", "Change mode of current inode to MODE", 2, 2, FL_WR, chmode }, { "chown", "Change owner of current inode to OWNER", 2, 2, FL_WR, chowner }, { "chgrp", "Change group of current inode to GROUP", 2, 2, FL_WR, chgroup }, { "chflags", "Change flags of current inode to FLAGS", 2, 2, FL_CWR, chaflags }, { "chgen", "Change generation number of current inode to GEN", 2, 2, FL_WR, chgen }, { "chsize", "Change size of current inode to SIZE", 2, 2, FL_CWR, chsize }, { "btime", "Change btime of current inode to BTIME", 2, 2, FL_WR, chbtime }, { "mtime", "Change mtime of current inode to MTIME", 2, 2, FL_WR, chmtime }, { "ctime", "Change ctime of current inode to CTIME", 2, 2, FL_WR, chctime }, { "atime", "Change atime of current inode to ATIME", 2, 2, FL_WR, chatime }, { "chdb", "Change db pointer N of current inode to BLKNO", 3, 3, FL_CWR, chdb }, { "quitclean", "Exit with filesystem marked clean", 1, 1, FL_RO, quitclean }, { "quit", "Exit", 1, 1, FL_RO, quit }, { "q", "Exit", 1, 1, FL_RO, quit }, { "exit", "Exit", 1, 1, FL_RO, quit }, { NULL, 0, 0, 0, 0, NULL }, }; int helpfn(int argc, char *argv[]) { struct cmdtable *cmdtp; printf("Commands are:\n%-10s %5s %5s %s\n", "command", "min args", "max args", "what"); for (cmdtp = cmds; cmdtp->cmd; cmdtp++) printf("%-10s %5u %5u %s\n", cmdtp->cmd, cmdtp->minargc-1, cmdtp->maxargc-1, cmdtp->helptxt); return 0; } char * prompt(EditLine *el) { static char pstring[64]; snprintf(pstring, sizeof(pstring), "fsdb (inum: %ju)> ", (uintmax_t)curinum); return pstring; } static void setcurinode(ino_t inum) { if (curip.i_number != 0) irelse(&curip); ginode(inum, &curip); curinode = curip.i_dp; curinum = inum; } int cmdloop(void) { char *line; const char *elline; int cmd_argc, rval = 0, known; #define scratch known char **cmd_argv; struct cmdtable *cmdp; History *hist; EditLine *elptr; HistEvent he; setcurinode(UFS_ROOTINO); printactive(0); hist = history_init(); history(hist, &he, H_SETSIZE, 100); /* 100 elt history buffer */ elptr = el_init("fsdb", stdin, stdout, stderr); el_set(elptr, EL_EDITOR, "emacs"); el_set(elptr, EL_PROMPT, prompt); el_set(elptr, EL_HIST, history, hist); el_source(elptr, NULL); while ((elline = el_gets(elptr, &scratch)) != NULL && scratch != 0) { if (debug) printf("command `%s'\n", elline); history(hist, &he, H_ENTER, elline); line = strdup(elline); cmd_argv = crack(line, &cmd_argc); /* * el_parse returns -1 to signal that it's not been handled * internally. */ if (el_parse(elptr, cmd_argc, (const char **)cmd_argv) != -1) continue; if (cmd_argc) { known = 0; for (cmdp = cmds; cmdp->cmd; cmdp++) { if (!strcmp(cmdp->cmd, cmd_argv[0])) { if ((cmdp->flags & (FL_CWR | FL_WR)) != 0 && nflag) warnx("`%s' requires write access", cmd_argv[0]), rval = 1; else if (cmd_argc >= cmdp->minargc && cmd_argc <= cmdp->maxargc) rval = (*cmdp->handler)(cmd_argc, cmd_argv); else if (cmd_argc >= cmdp->minargc && (cmdp->flags & FL_ST) == FL_ST) { strcpy(line, elline); cmd_argv = recrack(line, &cmd_argc, cmdp->maxargc); rval = (*cmdp->handler)(cmd_argc, cmd_argv); } else rval = argcount(cmdp, cmd_argc, cmd_argv); known = 1; if (rval == 0) { if ((cmdp->flags & FL_WR) != 0) fsnoncritmodified = 1; if ((cmdp->flags & FL_CWR) != 0) fscritmodified = 1; } break; } } if (!known) warnx("unknown command `%s'", cmd_argv[0]), rval = 1; } else rval = 0; free(line); if (rval < 0) { /* user typed "quit" */ irelse(&curip); return 0; } if (rval) warnx("command failed, return value was %d", rval); } el_end(elptr); history_end(hist); irelse(&curip); return rval; } #define GETINUM(ac,inum) inum = strtoul(argv[ac], &cp, 0); \ if (inum < UFS_ROOTINO || inum > maxino || cp == argv[ac] || *cp != '\0' ) { \ printf("inode %ju out of range; range is [%ju,%ju]\n", \ (uintmax_t)inum, (uintmax_t)UFS_ROOTINO, (uintmax_t)maxino);\ return 1; \ } /* * Focus on given inode number */ CMDFUNCSTART(focus) { ino_t inum; char *cp; GETINUM(1,inum); ocurrent = curinum; setcurinode(inum); printactive(0); return 0; } CMDFUNCSTART(back) { setcurinode(ocurrent); printactive(0); return 0; } CMDFUNCSTART(zapi) { struct inode ip; ino_t inum; char *cp; GETINUM(1,inum); ginode(inum, &ip); clearinode(ip.i_dp); inodirty(&ip); irelse(&ip); return 0; } CMDFUNCSTART(active) { printactive(0); return 0; } CMDFUNCSTART(blocks) { printactive(1); return 0; } CMDFUNCSTART(quit) { return -1; } CMDFUNCSTART(quitclean) { if (fscritmodified) { printf("Warning: modified filesystem marked clean\n"); fscritmodified = 0; sblock.fs_clean = 1; } return -1; } CMDFUNCSTART(uplink) { if (!checkactive()) return 1; DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) + 1); printf("inode %ju link count now %d\n", (uintmax_t)curinum, DIP(curinode, di_nlink)); inodirty(&curip); return 0; } CMDFUNCSTART(downlink) { if (!checkactive()) return 1; DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) - 1); printf("inode %ju link count now %d\n", (uintmax_t)curinum, DIP(curinode, di_nlink)); inodirty(&curip); return 0; } const char *typename[] = { "unknown", "fifo", "char special", "unregistered #3", "directory", "unregistered #5", "blk special", "unregistered #7", "regular", "unregistered #9", "symlink", "unregistered #11", "socket", "unregistered #13", "whiteout", }; int diroff; int slot; int scannames(struct inodesc *idesc) { struct direct *dirp = idesc->id_dirp; printf("slot %d off %d ino %d reclen %d: %s, `%.*s'\n", slot++, diroff, dirp->d_ino, dirp->d_reclen, typename[dirp->d_type], dirp->d_namlen, dirp->d_name); diroff += dirp->d_reclen; return (KEEPON); } CMDFUNCSTART(ls) { struct inodesc idesc; checkactivedir(); /* let it go on anyway */ slot = 0; diroff = 0; idesc.id_number = curinum; idesc.id_func = scannames; idesc.id_type = DATA; idesc.id_fix = IGNORE; ckinode(curinode, &idesc); return 0; } static int findblk_numtofind; static int wantedblksize; CMDFUNCSTART(findblk) { ino_t inum, inosused; uint32_t *wantedblk32; uint64_t *wantedblk64; struct bufarea *cgbp; struct cg *cgp; int c, i, is_ufs2; wantedblksize = (argc - 1); is_ufs2 = sblock.fs_magic == FS_UFS2_MAGIC; ocurrent = curinum; if (is_ufs2) { wantedblk64 = calloc(wantedblksize, sizeof(uint64_t)); if (wantedblk64 == NULL) err(1, "malloc"); for (i = 1; i < argc; i++) wantedblk64[i - 1] = dbtofsb(&sblock, strtoull(argv[i], NULL, 0)); } else { wantedblk32 = calloc(wantedblksize, sizeof(uint32_t)); if (wantedblk32 == NULL) err(1, "malloc"); for (i = 1; i < argc; i++) wantedblk32[i - 1] = dbtofsb(&sblock, strtoull(argv[i], NULL, 0)); } findblk_numtofind = wantedblksize; /* * sblock.fs_ncg holds a number of cylinder groups. * Iterate over all cylinder groups. */ for (c = 0; c < sblock.fs_ncg; c++) { /* * sblock.fs_ipg holds a number of inodes per cylinder group. * Calculate a highest inode number for a given cylinder group. */ inum = c * sblock.fs_ipg; /* Read cylinder group. */ cgbp = cglookup(c); cgp = cgbp->b_un.b_cg; /* * Get a highest used inode number for a given cylinder group. * For UFS1 all inodes initialized at the newfs stage. */ if (is_ufs2) inosused = cgp->cg_initediblk; else inosused = sblock.fs_ipg; for (; inosused > 0; inum++, inosused--) { /* Skip magic inodes: 0, UFS_WINO, UFS_ROOTINO. */ if (inum < UFS_ROOTINO) continue; /* * Check if the block we are looking for is just an inode block. * * ino_to_fsba() - get block containing inode from its number. * INOPB() - get a number of inodes in one disk block. */ if (is_ufs2 ? compare_blk64(wantedblk64, ino_to_fsba(&sblock, inum)) : compare_blk32(wantedblk32, ino_to_fsba(&sblock, inum))) { printf("block %llu: inode block (%ju-%ju)\n", (unsigned long long)fsbtodb(&sblock, ino_to_fsba(&sblock, inum)), (uintmax_t)(inum / INOPB(&sblock)) * INOPB(&sblock), (uintmax_t)(inum / INOPB(&sblock) + 1) * INOPB(&sblock)); findblk_numtofind--; if (findblk_numtofind == 0) goto end; } /* Get on-disk inode aka dinode. */ setcurinode(inum); /* Find IFLNK dinode with allocated data blocks. */ switch (DIP(curinode, di_mode) & IFMT) { case IFDIR: case IFREG: if (DIP(curinode, di_blocks) == 0) continue; break; case IFLNK: { uint64_t size = DIP(curinode, di_size); if (size > 0 && size < sblock.fs_maxsymlinklen && DIP(curinode, di_blocks) == 0) continue; else break; } default: continue; } /* Look through direct data blocks. */ if (is_ufs2 ? find_blks64(curinode->dp2.di_db, UFS_NDADDR, wantedblk64) : find_blks32(curinode->dp1.di_db, UFS_NDADDR, wantedblk32)) goto end; for (i = 0; i < UFS_NIADDR; i++) { /* * Does the block we are looking for belongs to the * indirect blocks? */ if (is_ufs2 ? compare_blk64(wantedblk64, curinode->dp2.di_ib[i]) : compare_blk32(wantedblk32, curinode->dp1.di_ib[i])) if (founddatablk(is_ufs2 ? curinode->dp2.di_ib[i] : curinode->dp1.di_ib[i])) goto end; /* * Search through indirect, double and triple indirect * data blocks. */ if (is_ufs2 ? (curinode->dp2.di_ib[i] != 0) : (curinode->dp1.di_ib[i] != 0)) if (is_ufs2 ? find_indirblks64(curinode->dp2.di_ib[i], i, wantedblk64) : find_indirblks32(curinode->dp1.di_ib[i], i, wantedblk32)) goto end; } } } end: setcurinode(ocurrent); if (is_ufs2) free(wantedblk64); else free(wantedblk32); return 0; } static int compare_blk32(uint32_t *wantedblk, uint32_t curblk) { int i; for (i = 0; i < wantedblksize; i++) { if (wantedblk[i] != 0 && wantedblk[i] == curblk) { wantedblk[i] = 0; return 1; } } return 0; } static int compare_blk64(uint64_t *wantedblk, uint64_t curblk) { int i; for (i = 0; i < wantedblksize; i++) { if (wantedblk[i] != 0 && wantedblk[i] == curblk) { wantedblk[i] = 0; return 1; } } return 0; } static int founddatablk(uint64_t blk) { printf("%llu: data block of inode %ju\n", (unsigned long long)fsbtodb(&sblock, blk), (uintmax_t)curinum); findblk_numtofind--; if (findblk_numtofind == 0) return 1; return 0; } static int find_blks32(uint32_t *buf, int size, uint32_t *wantedblk) { int blk; for (blk = 0; blk < size; blk++) { if (buf[blk] == 0) continue; if (compare_blk32(wantedblk, buf[blk])) { if (founddatablk(buf[blk])) return 1; } } return 0; } static int find_indirblks32(uint32_t blk, int ind_level, uint32_t *wantedblk) { #define MAXNINDIR (MAXBSIZE / sizeof(uint32_t)) uint32_t idblk[MAXNINDIR]; int i; blread(fsreadfd, (char *)idblk, fsbtodb(&sblock, blk), (int)sblock.fs_bsize); if (ind_level <= 0) { if (find_blks32(idblk, sblock.fs_bsize / sizeof(uint32_t), wantedblk)) return 1; } else { ind_level--; for (i = 0; i < sblock.fs_bsize / sizeof(uint32_t); i++) { if (compare_blk32(wantedblk, idblk[i])) { if (founddatablk(idblk[i])) return 1; } if (idblk[i] != 0) if (find_indirblks32(idblk[i], ind_level, wantedblk)) return 1; } } #undef MAXNINDIR return 0; } static int find_blks64(uint64_t *buf, int size, uint64_t *wantedblk) { int blk; for (blk = 0; blk < size; blk++) { if (buf[blk] == 0) continue; if (compare_blk64(wantedblk, buf[blk])) { if (founddatablk(buf[blk])) return 1; } } return 0; } static int find_indirblks64(uint64_t blk, int ind_level, uint64_t *wantedblk) { #define MAXNINDIR (MAXBSIZE / sizeof(uint64_t)) uint64_t idblk[MAXNINDIR]; int i; blread(fsreadfd, (char *)idblk, fsbtodb(&sblock, blk), (int)sblock.fs_bsize); if (ind_level <= 0) { if (find_blks64(idblk, sblock.fs_bsize / sizeof(uint64_t), wantedblk)) return 1; } else { ind_level--; for (i = 0; i < sblock.fs_bsize / sizeof(uint64_t); i++) { if (compare_blk64(wantedblk, idblk[i])) { if (founddatablk(idblk[i])) return 1; } if (idblk[i] != 0) if (find_indirblks64(idblk[i], ind_level, wantedblk)) return 1; } } #undef MAXNINDIR return 0; } int findino(struct inodesc *idesc); /* from fsck */ static int dolookup(char *name); static int dolookup(char *name) { struct inodesc idesc; if (!checkactivedir()) return 0; idesc.id_number = curinum; idesc.id_func = findino; idesc.id_name = name; idesc.id_type = DATA; idesc.id_fix = IGNORE; if (ckinode(curinode, &idesc) & FOUND) { setcurinode(idesc.id_parent); printactive(0); return 1; } else { warnx("name `%s' not found in current inode directory", name); return 0; } } CMDFUNCSTART(focusname) { char *p, *val; if (!checkactive()) return 1; ocurrent = curinum; if (argv[1][0] == '/') { setcurinode(UFS_ROOTINO); } else { if (!checkactivedir()) return 1; } for (p = argv[1]; p != NULL;) { while ((val = strsep(&p, "/")) != NULL && *val == '\0'); if (val) { printf("component `%s': ", val); fflush(stdout); if (!dolookup(val)) { return(1); } } } return 0; } CMDFUNCSTART(ln) { ino_t inum; int rval; char *cp; GETINUM(1,inum); if (!checkactivedir()) return 1; rval = makeentry(curinum, inum, argv[2]); if (rval) printf("Ino %ju entered as `%s'\n", (uintmax_t)inum, argv[2]); else printf("could not enter name? weird.\n"); return rval; } CMDFUNCSTART(rm) { int rval; if (!checkactivedir()) return 1; rval = changeino(curinum, argv[1], 0, 0); if (rval & ALTERED) { printf("Name `%s' removed\n", argv[1]); return 0; } else { printf("could not remove name ('%s')? weird.\n", argv[1]); return 1; } } long slotcount, desired; int chinumfunc(struct inodesc *idesc) { struct direct *dirp = idesc->id_dirp; if (slotcount++ == desired) { dirp->d_ino = idesc->id_parent; return STOP|ALTERED|FOUND; } return KEEPON; } CMDFUNCSTART(chinum) { char *cp; ino_t inum; struct inodesc idesc; slotcount = 0; if (!checkactivedir()) return 1; GETINUM(2,inum); desired = strtol(argv[1], &cp, 0); if (cp == argv[1] || *cp != '\0' || desired < 0) { printf("invalid slot number `%s'\n", argv[1]); return 1; } idesc.id_number = curinum; idesc.id_func = chinumfunc; idesc.id_fix = IGNORE; idesc.id_type = DATA; idesc.id_parent = inum; /* XXX convenient hiding place */ if (ckinode(curinode, &idesc) & FOUND) return 0; else { warnx("no %sth slot in current directory", argv[1]); return 1; } } int chnamefunc(struct inodesc *idesc) { struct direct *dirp = idesc->id_dirp; struct direct testdir; if (slotcount++ == desired) { /* will name fit? */ testdir.d_namlen = strlen(idesc->id_name); if (DIRSIZ(NEWDIRFMT, &testdir) <= dirp->d_reclen) { dirp->d_namlen = testdir.d_namlen; strcpy(dirp->d_name, idesc->id_name); return STOP|ALTERED|FOUND; } else return STOP|FOUND; /* won't fit, so give up */ } return KEEPON; } CMDFUNCSTART(chname) { int rval; char *cp; struct inodesc idesc; slotcount = 0; if (!checkactivedir()) return 1; desired = strtoul(argv[1], &cp, 0); if (cp == argv[1] || *cp != '\0') { printf("invalid slot number `%s'\n", argv[1]); return 1; } idesc.id_number = curinum; idesc.id_func = chnamefunc; idesc.id_fix = IGNORE; idesc.id_type = DATA; idesc.id_name = argv[2]; rval = ckinode(curinode, &idesc); if ((rval & (FOUND|ALTERED)) == (FOUND|ALTERED)) return 0; else if (rval & FOUND) { warnx("new name `%s' does not fit in slot %s\n", argv[2], argv[1]); return 1; } else { warnx("no %sth slot in current directory", argv[1]); return 1; } } struct typemap { const char *typename; int typebits; } typenamemap[] = { {"file", IFREG}, {"dir", IFDIR}, {"socket", IFSOCK}, {"fifo", IFIFO}, }; CMDFUNCSTART(newtype) { int type; struct typemap *tp; if (!checkactive()) return 1; type = DIP(curinode, di_mode) & IFMT; for (tp = typenamemap; tp < &typenamemap[nitems(typenamemap)]; tp++) { if (!strcmp(argv[1], tp->typename)) { printf("setting type to %s\n", tp->typename); type = tp->typebits; break; } } if (tp == &typenamemap[nitems(typenamemap)]) { warnx("type `%s' not known", argv[1]); warnx("try one of `file', `dir', `socket', `fifo'"); return 1; } DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~IFMT); DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | type); inodirty(&curip); printactive(0); return 0; } CMDFUNCSTART(chmode) { long modebits; char *cp; if (!checkactive()) return 1; modebits = strtol(argv[1], &cp, 8); if (cp == argv[1] || *cp != '\0' || (modebits & ~07777)) { warnx("bad modebits `%s'", argv[1]); return 1; } DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~07777); DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | modebits); inodirty(&curip); printactive(0); return 0; } CMDFUNCSTART(chaflags) { u_long flags; char *cp; if (!checkactive()) return 1; flags = strtoul(argv[1], &cp, 0); if (cp == argv[1] || *cp != '\0' ) { warnx("bad flags `%s'", argv[1]); return 1; } if (flags > UINT_MAX) { warnx("flags set beyond 32-bit range of field (%lx)\n", flags); return(1); } DIP_SET(curinode, di_flags, flags); inodirty(&curip); printactive(0); return 0; } CMDFUNCSTART(chgen) { long gen; char *cp; if (!checkactive()) return 1; gen = strtol(argv[1], &cp, 0); if (cp == argv[1] || *cp != '\0' ) { warnx("bad gen `%s'", argv[1]); return 1; } if (gen > UINT_MAX) { warnx("gen set beyond 32-bit range of field (0x%lx), max is 0x%x\n", gen, UINT_MAX); return(1); } DIP_SET(curinode, di_gen, gen); inodirty(&curip); printactive(0); return 0; } CMDFUNCSTART(chsize) { off_t size; char *cp; if (!checkactive()) return 1; size = strtoll(argv[1], &cp, 0); if (cp == argv[1] || *cp != '\0') { warnx("bad size `%s'", argv[1]); return 1; } if (size < 0) { warnx("size set to negative (%jd)\n", (intmax_t)size); return(1); } DIP_SET(curinode, di_size, size); inodirty(&curip); printactive(0); return 0; } CMDFUNC(chdb) { unsigned int idx; daddr_t bno; char *cp; if (!checkactive()) return 1; idx = strtoull(argv[1], &cp, 0); if (cp == argv[1] || *cp != '\0') { warnx("bad pointer idx `%s'", argv[1]); return 1; } bno = strtoll(argv[2], &cp, 0); if (cp == argv[2] || *cp != '\0') { warnx("bad block number `%s'", argv[2]); return 1; } if (idx >= UFS_NDADDR) { warnx("pointer index %d is out of range", idx); return 1; } DIP_SET(curinode, di_db[idx], bno); inodirty(&curip); printactive(0); return 0; } CMDFUNCSTART(linkcount) { int lcnt; char *cp; if (!checkactive()) return 1; lcnt = strtol(argv[1], &cp, 0); if (cp == argv[1] || *cp != '\0' ) { warnx("bad link count `%s'", argv[1]); return 1; } if (lcnt > USHRT_MAX || lcnt < 0) { warnx("max link count is %d\n", USHRT_MAX); return 1; } DIP_SET(curinode, di_nlink, lcnt); inodirty(&curip); printactive(0); return 0; } CMDFUNCSTART(chowner) { unsigned long uid; char *cp; struct passwd *pwd; if (!checkactive()) return 1; uid = strtoul(argv[1], &cp, 0); if (cp == argv[1] || *cp != '\0' ) { /* try looking up name */ if ((pwd = getpwnam(argv[1]))) { uid = pwd->pw_uid; } else { warnx("bad uid `%s'", argv[1]); return 1; } } DIP_SET(curinode, di_uid, uid); inodirty(&curip); printactive(0); return 0; } CMDFUNCSTART(chgroup) { unsigned long gid; char *cp; struct group *grp; if (!checkactive()) return 1; gid = strtoul(argv[1], &cp, 0); if (cp == argv[1] || *cp != '\0' ) { if ((grp = getgrnam(argv[1]))) { gid = grp->gr_gid; } else { warnx("bad gid `%s'", argv[1]); return 1; } } DIP_SET(curinode, di_gid, gid); inodirty(&curip); printactive(0); return 0; } int dotime(char *name, time_t *secp, int32_t *nsecp) { char *p, *val; struct tm t; int32_t nsec; p = strchr(name, '.'); if (p) { *p = '\0'; nsec = strtoul(++p, &val, 0); if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) { warnx("invalid nanoseconds"); goto badformat; } } else nsec = 0; if (strlen(name) != 14) { badformat: warnx("date format: YYYYMMDDHHMMSS[.nsec]"); return 1; } *nsecp = nsec; for (p = name; *p; p++) if (*p < '0' || *p > '9') goto badformat; p = name; #define VAL() ((*p++) - '0') t.tm_year = VAL(); t.tm_year = VAL() + t.tm_year * 10; t.tm_year = VAL() + t.tm_year * 10; t.tm_year = VAL() + t.tm_year * 10 - 1900; t.tm_mon = VAL(); t.tm_mon = VAL() + t.tm_mon * 10 - 1; t.tm_mday = VAL(); t.tm_mday = VAL() + t.tm_mday * 10; t.tm_hour = VAL(); t.tm_hour = VAL() + t.tm_hour * 10; t.tm_min = VAL(); t.tm_min = VAL() + t.tm_min * 10; t.tm_sec = VAL(); t.tm_sec = VAL() + t.tm_sec * 10; t.tm_isdst = -1; *secp = mktime(&t); if (*secp == -1) { warnx("date/time out of range"); return 1; } return 0; } CMDFUNCSTART(chbtime) { time_t secs; int32_t nsecs; if (dotime(argv[1], &secs, &nsecs)) return 1; if (sblock.fs_magic == FS_UFS1_MAGIC) return 1; curinode->dp2.di_birthtime = _time_to_time64(secs); curinode->dp2.di_birthnsec = nsecs; inodirty(&curip); printactive(0); return 0; } CMDFUNCSTART(chmtime) { time_t secs; int32_t nsecs; if (dotime(argv[1], &secs, &nsecs)) return 1; if (sblock.fs_magic == FS_UFS1_MAGIC) curinode->dp1.di_mtime = _time_to_time32(secs); else curinode->dp2.di_mtime = _time_to_time64(secs); DIP_SET(curinode, di_mtimensec, nsecs); inodirty(&curip); printactive(0); return 0; } CMDFUNCSTART(chatime) { time_t secs; int32_t nsecs; if (dotime(argv[1], &secs, &nsecs)) return 1; if (sblock.fs_magic == FS_UFS1_MAGIC) curinode->dp1.di_atime = _time_to_time32(secs); else curinode->dp2.di_atime = _time_to_time64(secs); DIP_SET(curinode, di_atimensec, nsecs); inodirty(&curip); printactive(0); return 0; } CMDFUNCSTART(chctime) { time_t secs; int32_t nsecs; if (dotime(argv[1], &secs, &nsecs)) return 1; if (sblock.fs_magic == FS_UFS1_MAGIC) curinode->dp1.di_ctime = _time_to_time32(secs); else curinode->dp2.di_ctime = _time_to_time64(secs); DIP_SET(curinode, di_ctimensec, nsecs); inodirty(&curip); printactive(0); return 0; } diff --git a/sbin/fsdb/fsdbutil.c b/sbin/fsdb/fsdbutil.c index c8a3a8a525e3..737dabba643f 100644 --- a/sbin/fsdb/fsdbutil.c +++ b/sbin/fsdb/fsdbutil.c @@ -1,250 +1,245 @@ /* $NetBSD: fsdbutil.c,v 1.2 1995/10/08 23:18:12 thorpej Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1995 John T. Kohl * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include "fsdb.h" #include "fsck.h" void prtblknos(struct fs *fs, union dinode *dp); char ** crack(char *line, int *argc) { static char *argv[8]; int i; char *p, *val; for (p = line, i = 0; p != NULL && i < 8; i++) { while ((val = strsep(&p, " \t\n")) != NULL && *val == '\0') /**/; if (val) argv[i] = val; else break; } *argc = i; return argv; } char ** recrack(char *line, int *argc, int argc_max) { static char *argv[8]; int i; char *p, *val; for (p = line, i = 0; p != NULL && i < 8 && i < argc_max - 1; i++) { while ((val = strsep(&p, " \t\n")) != NULL && *val == '\0') /**/; if (val) argv[i] = val; else break; } argv[i] = argv[i - 1] + strlen(argv[i - 1]) + 1; argv[i][strcspn(argv[i], "\n")] = '\0'; *argc = i + 1; return argv; } int argcount(struct cmdtable *cmdp, int argc, char *argv[]) { if (cmdp->minargc == cmdp->maxargc) warnx("command `%s' takes %u arguments, got %u", cmdp->cmd, cmdp->minargc-1, argc-1); else warnx("command `%s' takes from %u to %u arguments", cmdp->cmd, cmdp->minargc-1, cmdp->maxargc-1); warnx("usage: %s: %s", cmdp->cmd, cmdp->helptxt); return 1; } void printstat(const char *cp, ino_t inum, union dinode *dp) { struct group *grp; struct passwd *pw; ufs2_daddr_t blocks; int64_t gen; char *p; time_t t; printf("%s: ", cp); switch (DIP(dp, di_mode) & IFMT) { case IFDIR: puts("directory"); break; case IFREG: puts("regular file"); break; case IFBLK: printf("block special (%#jx)", (uintmax_t)DIP(dp, di_rdev)); break; case IFCHR: printf("character special (%#jx)", DIP(dp, di_rdev)); break; case IFLNK: fputs("symlink",stdout); if (DIP(dp, di_size) > 0 && DIP(dp, di_size) < sblock.fs_maxsymlinklen && DIP(dp, di_blocks) == 0) { printf(" to `%.*s'\n", (int) DIP(dp, di_size), DIP(dp, di_shortlink)); } else { putchar('\n'); } break; case IFSOCK: puts("socket"); break; case IFIFO: puts("fifo"); break; } printf("I=%ju MODE=%o SIZE=%ju", (uintmax_t)inum, DIP(dp, di_mode), (uintmax_t)DIP(dp, di_size)); if (sblock.fs_magic != FS_UFS1_MAGIC) { t = _time64_to_time(dp->dp2.di_birthtime); p = ctime(&t); printf("\n\tBTIME=%15.15s %4.4s [%d nsec]", &p[4], &p[20], dp->dp2.di_birthnsec); } if (sblock.fs_magic == FS_UFS1_MAGIC) t = _time32_to_time(dp->dp1.di_mtime); else t = _time64_to_time(dp->dp2.di_mtime); p = ctime(&t); printf("\n\tMTIME=%15.15s %4.4s [%d nsec]", &p[4], &p[20], DIP(dp, di_mtimensec)); if (sblock.fs_magic == FS_UFS1_MAGIC) t = _time32_to_time(dp->dp1.di_ctime); else t = _time64_to_time(dp->dp2.di_ctime); p = ctime(&t); printf("\n\tCTIME=%15.15s %4.4s [%d nsec]", &p[4], &p[20], DIP(dp, di_ctimensec)); if (sblock.fs_magic == FS_UFS1_MAGIC) t = _time32_to_time(dp->dp1.di_atime); else t = _time64_to_time(dp->dp2.di_atime); p = ctime(&t); printf("\n\tATIME=%15.15s %4.4s [%d nsec]\n", &p[4], &p[20], DIP(dp, di_atimensec)); if ((pw = getpwuid(DIP(dp, di_uid)))) printf("OWNER=%s ", pw->pw_name); else printf("OWNUID=%u ", DIP(dp, di_uid)); if ((grp = getgrgid(DIP(dp, di_gid)))) printf("GRP=%s ", grp->gr_name); else printf("GID=%u ", DIP(dp, di_gid)); blocks = DIP(dp, di_blocks); gen = DIP(dp, di_gen); printf("LINKCNT=%d FLAGS=%#x BLKCNT=%jx GEN=%jx\n", DIP(dp, di_nlink), DIP(dp, di_flags), (intmax_t)blocks, (intmax_t)gen); } int checkactive(void) { if (!curinode) { warnx("no current inode\n"); return 0; } return 1; } int checkactivedir(void) { if (!curinode) { warnx("no current inode\n"); return 0; } if ((DIP(curinode, di_mode) & IFMT) != IFDIR) { warnx("inode %ju not a directory", (uintmax_t)curinum); return 0; } return 1; } int printactive(int doblocks) { if (!checkactive()) return 1; switch (DIP(curinode, di_mode) & IFMT) { case IFDIR: case IFREG: case IFBLK: case IFCHR: case IFLNK: case IFSOCK: case IFIFO: if (doblocks) prtblknos(&sblock, curinode); else printstat("current inode", curinum, curinode); break; case 0: printf("current inode %ju: unallocated inode\n", (uintmax_t)curinum); break; default: printf("current inode %ju: screwy itype 0%o (mode 0%o)?\n", (uintmax_t)curinum, DIP(curinode, di_mode) & IFMT, DIP(curinode, di_mode)); break; } return 0; } diff --git a/sbin/fsirand/fsirand.c b/sbin/fsirand/fsirand.c index cd60093e3642..2a5eb0c5136d 100644 --- a/sbin/fsirand/fsirand.c +++ b/sbin/fsirand/fsirand.c @@ -1,240 +1,235 @@ /* $OpenBSD: fsirand.c,v 1.9 1997/02/28 00:46:33 millert Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 1997 Todd C. Miller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Todd C. Miller. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void usage(void) __dead2; int fsirand(char *); static int printonly = 0, force = 0, ignorelabel = 0; int main(int argc, char *argv[]) { int n, ex = 0; struct rlimit rl; while ((n = getopt(argc, argv, "bfp")) != -1) { switch (n) { case 'b': ignorelabel++; break; case 'p': printonly++; break; case 'f': force++; break; default: usage(); } } if (argc - optind < 1) usage(); /* Increase our data size to the max */ if (getrlimit(RLIMIT_DATA, &rl) == 0) { rl.rlim_cur = rl.rlim_max; if (setrlimit(RLIMIT_DATA, &rl) < 0) warn("can't get resource limit to max data size"); } else warn("can't get resource limit for data size"); for (n = optind; n < argc; n++) { if (argc - optind != 1) (void)puts(argv[n]); ex += fsirand(argv[n]); if (n < argc - 1) putchar('\n'); } exit(ex); } int fsirand(char *device) { struct ufs1_dinode *dp1; struct ufs2_dinode *dp2; caddr_t inodebuf; ssize_t ibufsize; struct fs *sblock; ino_t inumber; ufs2_daddr_t dblk; int devfd, n, cg; u_int32_t bsize = DEV_BSIZE; if ((devfd = open(device, printonly ? O_RDONLY : O_RDWR)) < 0) { warn("can't open %s", device); return (1); } dp1 = NULL; dp2 = NULL; /* Read in master superblock */ if ((errno = sbget(devfd, &sblock, UFS_STDSB, UFS_NOCSUM)) != 0) { switch (errno) { case ENOENT: warnx("Cannot find file system superblock"); return (1); default: warn("Unable to read file system superblock"); return (1); } } /* * Check for unclean filesystem. */ if (sblock->fs_clean == 0 || (sblock->fs_flags & (FS_UNCLEAN | FS_NEEDSFSCK)) != 0) errx(1, "%s is not clean - run fsck.\n", device); if (sblock->fs_magic == FS_UFS1_MAGIC && sblock->fs_old_inodefmt < FS_44INODEFMT) { warnx("file system format is too old, sorry"); return (1); } if (!force && !printonly && sblock->fs_clean != 1) { warnx("file system is not clean, fsck %s first", device); return (1); } /* XXX - should really cap buffer at 512kb or so */ if (sblock->fs_magic == FS_UFS1_MAGIC) ibufsize = sizeof(struct ufs1_dinode) * sblock->fs_ipg; else ibufsize = sizeof(struct ufs2_dinode) * sblock->fs_ipg; if ((inodebuf = malloc(ibufsize)) == NULL) errx(1, "can't allocate memory for inode buffer"); if (printonly && (sblock->fs_id[0] || sblock->fs_id[1])) { if (sblock->fs_id[0]) (void)printf("%s was randomized on %s", device, ctime((void *)&(sblock->fs_id[0]))); (void)printf("fsid: %x %x\n", sblock->fs_id[0], sblock->fs_id[1]); } /* Randomize fs_id unless old 4.2BSD file system */ if (!printonly) { /* Randomize fs_id and write out new sblock and backups */ sblock->fs_id[0] = (u_int32_t)time(NULL); sblock->fs_id[1] = arc4random(); if (sbput(devfd, sblock, sblock->fs_ncg) != 0) { warn("could not write updated superblock"); return (1); } } /* For each cylinder group, randomize inodes and update backup sblock */ for (cg = 0, inumber = UFS_ROOTINO; cg < (int)sblock->fs_ncg; cg++) { /* Read in inodes, then print or randomize generation nums */ dblk = fsbtodb(sblock, ino_to_fsba(sblock, inumber)); if (lseek(devfd, (off_t)dblk * bsize, SEEK_SET) < 0) { warn("can't seek to %jd", (intmax_t)dblk * bsize); return (1); } else if ((n = read(devfd, inodebuf, ibufsize)) != ibufsize) { warnx("can't read inodes: %s", (n < ibufsize) ? "short read" : strerror(errno)); return (1); } dp1 = (struct ufs1_dinode *)(void *)inodebuf; dp2 = (struct ufs2_dinode *)(void *)inodebuf; for (n = cg > 0 ? 0 : UFS_ROOTINO; n < (int)sblock->fs_ipg; n++, inumber++) { if (printonly) { (void)printf("ino %ju gen %08x\n", (uintmax_t)inumber, sblock->fs_magic == FS_UFS1_MAGIC ? dp1->di_gen : dp2->di_gen); } else if (sblock->fs_magic == FS_UFS1_MAGIC) { dp1->di_gen = arc4random(); dp1++; } else { dp2->di_gen = arc4random(); ffs_update_dinode_ckhash(sblock, dp2); dp2++; } } /* Write out modified inodes */ if (!printonly) { if (lseek(devfd, (off_t)dblk * bsize, SEEK_SET) < 0) { warn("can't seek to %jd", (intmax_t)dblk * bsize); return (1); } else if ((n = write(devfd, inodebuf, ibufsize)) != ibufsize) { warnx("can't write inodes: %s", (n != ibufsize) ? "short write" : strerror(errno)); return (1); } } } (void)close(devfd); return(0); } static void usage(void) { (void)fprintf(stderr, "usage: fsirand [-b] [-f] [-p] special [special ...]\n"); exit(1); } diff --git a/sbin/growfs/debug.c b/sbin/growfs/debug.c index e0dfc997fcf6..456e67dbc5c2 100644 --- a/sbin/growfs/debug.c +++ b/sbin/growfs/debug.c @@ -1,844 +1,839 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz * Copyright (c) 1980, 1989, 1993 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Christoph Herrmann and Thomas-Henning von Kamptz, Munich and Frankfurt. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgment: * This product includes software developed by the University of * California, Berkeley and its contributors, as well as Christoph * Herrmann and Thomas-Henning von Kamptz. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $TSHeader: src/sbin/growfs/debug.c,v 1.3 2000/12/12 19:31:00 tomsoft Exp $ * */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include "debug.h" #ifdef FS_DEBUG static FILE *dbg_log = NULL; static unsigned int indent = 0; /* * prototypes not done here, as they come with debug.h */ /* * Open the filehandle where all debug output has to go. */ void dbg_open(const char *fn) { if (strcmp(fn, "-") == 0) dbg_log = fopen("/dev/stdout", "a"); else dbg_log = fopen(fn, "a"); return; } /* * Close the filehandle where all debug output went to. */ void dbg_close(void) { if (dbg_log) { fclose(dbg_log); dbg_log = NULL; } return; } /* * Dump out a full file system block in hex. */ void dbg_dump_hex(struct fs *sb, const char *comment, unsigned char *mem) { int i, j, k; if (!dbg_log) return; fprintf(dbg_log, "===== START HEXDUMP =====\n"); fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)mem, comment); indent++; for (i = 0; i < sb->fs_bsize; i += 24) { for (j = 0; j < 3; j++) { for (k = 0; k < 8; k++) fprintf(dbg_log, "%02x ", *mem++); fprintf(dbg_log, " "); } fprintf(dbg_log, "\n"); } indent--; fprintf(dbg_log, "===== END HEXDUMP =====\n"); return; } /* * Dump the superblock. */ void dbg_dump_fs(struct fs *sb, const char *comment) { int j; if (!dbg_log) return; fprintf(dbg_log, "===== START SUPERBLOCK =====\n"); fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)sb, comment); indent++; fprintf(dbg_log, "sblkno int32_t 0x%08x\n", sb->fs_sblkno); fprintf(dbg_log, "cblkno int32_t 0x%08x\n", sb->fs_cblkno); fprintf(dbg_log, "iblkno int32_t 0x%08x\n", sb->fs_iblkno); fprintf(dbg_log, "dblkno int32_t 0x%08x\n", sb->fs_dblkno); fprintf(dbg_log, "old_cgoffset int32_t 0x%08x\n", sb->fs_old_cgoffset); fprintf(dbg_log, "old_cgmask int32_t 0x%08x\n", sb->fs_old_cgmask); fprintf(dbg_log, "old_time int32_t %10u\n", (unsigned int)sb->fs_old_time); fprintf(dbg_log, "old_size int32_t 0x%08x\n", sb->fs_old_size); fprintf(dbg_log, "old_dsize int32_t 0x%08x\n", sb->fs_old_dsize); fprintf(dbg_log, "ncg int32_t 0x%08x\n", sb->fs_ncg); fprintf(dbg_log, "bsize int32_t 0x%08x\n", sb->fs_bsize); fprintf(dbg_log, "fsize int32_t 0x%08x\n", sb->fs_fsize); fprintf(dbg_log, "frag int32_t 0x%08x\n", sb->fs_frag); fprintf(dbg_log, "minfree int32_t 0x%08x\n", sb->fs_minfree); fprintf(dbg_log, "old_rotdelay int32_t 0x%08x\n", sb->fs_old_rotdelay); fprintf(dbg_log, "old_rps int32_t 0x%08x\n", sb->fs_old_rps); fprintf(dbg_log, "bmask int32_t 0x%08x\n", sb->fs_bmask); fprintf(dbg_log, "fmask int32_t 0x%08x\n", sb->fs_fmask); fprintf(dbg_log, "bshift int32_t 0x%08x\n", sb->fs_bshift); fprintf(dbg_log, "fshift int32_t 0x%08x\n", sb->fs_fshift); fprintf(dbg_log, "maxcontig int32_t 0x%08x\n", sb->fs_maxcontig); fprintf(dbg_log, "maxbpg int32_t 0x%08x\n", sb->fs_maxbpg); fprintf(dbg_log, "fragshift int32_t 0x%08x\n", sb->fs_fragshift); fprintf(dbg_log, "fsbtodb int32_t 0x%08x\n", sb->fs_fsbtodb); fprintf(dbg_log, "sbsize int32_t 0x%08x\n", sb->fs_sbsize); fprintf(dbg_log, "spare1 int32_t[2] 0x%08x 0x%08x\n", sb->fs_spare1[0], sb->fs_spare1[1]); fprintf(dbg_log, "nindir int32_t 0x%08x\n", sb->fs_nindir); fprintf(dbg_log, "inopb int32_t 0x%08x\n", sb->fs_inopb); fprintf(dbg_log, "old_nspf int32_t 0x%08x\n", sb->fs_old_nspf); fprintf(dbg_log, "optim int32_t 0x%08x\n", sb->fs_optim); fprintf(dbg_log, "old_npsect int32_t 0x%08x\n", sb->fs_old_npsect); fprintf(dbg_log, "old_interleave int32_t 0x%08x\n", sb->fs_old_interleave); fprintf(dbg_log, "old_trackskew int32_t 0x%08x\n", sb->fs_old_trackskew); fprintf(dbg_log, "id int32_t[2] 0x%08x 0x%08x\n", sb->fs_id[0], sb->fs_id[1]); fprintf(dbg_log, "old_csaddr int32_t 0x%08x\n", sb->fs_old_csaddr); fprintf(dbg_log, "cssize int32_t 0x%08x\n", sb->fs_cssize); fprintf(dbg_log, "cgsize int32_t 0x%08x\n", sb->fs_cgsize); fprintf(dbg_log, "spare2 int32_t 0x%08x\n", sb->fs_spare2); fprintf(dbg_log, "old_nsect int32_t 0x%08x\n", sb->fs_old_nsect); fprintf(dbg_log, "old_spc int32_t 0x%08x\n", sb->fs_old_spc); fprintf(dbg_log, "old_ncyl int32_t 0x%08x\n", sb->fs_old_ncyl); fprintf(dbg_log, "old_cpg int32_t 0x%08x\n", sb->fs_old_cpg); fprintf(dbg_log, "ipg int32_t 0x%08x\n", sb->fs_ipg); fprintf(dbg_log, "fpg int32_t 0x%08x\n", sb->fs_fpg); dbg_dump_csum("internal old_cstotal", &sb->fs_old_cstotal); fprintf(dbg_log, "fmod int8_t 0x%02x\n", sb->fs_fmod); fprintf(dbg_log, "clean int8_t 0x%02x\n", sb->fs_clean); fprintf(dbg_log, "ronly int8_t 0x%02x\n", sb->fs_ronly); fprintf(dbg_log, "old_flags int8_t 0x%02x\n", sb->fs_old_flags); fprintf(dbg_log, "fsmnt u_char[MAXMNTLEN] \"%s\"\n", sb->fs_fsmnt); fprintf(dbg_log, "volname u_char[MAXVOLLEN] \"%s\"\n", sb->fs_volname); fprintf(dbg_log, "swuid u_int64_t 0x%08x%08x\n", ((unsigned int *)&(sb->fs_swuid))[1], ((unsigned int *)&(sb->fs_swuid))[0]); fprintf(dbg_log, "pad int32_t 0x%08x\n", sb->fs_pad); fprintf(dbg_log, "cgrotor int32_t 0x%08x\n", sb->fs_cgrotor); /* * struct csum[MAXCSBUFS] - is only maintained in memory */ /* fprintf(dbg_log, " int32_t\n", sb->*fs_maxcluster);*/ fprintf(dbg_log, "old_cpc int32_t 0x%08x\n", sb->fs_old_cpc); /* * int16_t fs_opostbl[16][8] - is dumped when used in dbg_dump_sptbl */ fprintf(dbg_log, "maxbsize int32_t 0x%08x\n", sb->fs_maxbsize); fprintf(dbg_log, "unrefs int64_t 0x%08jx\n", sb->fs_unrefs); fprintf(dbg_log, "sblockloc int64_t 0x%08x%08x\n", ((unsigned int *)&(sb->fs_sblockloc))[1], ((unsigned int *)&(sb->fs_sblockloc))[0]); dbg_dump_csum_total("internal cstotal", &sb->fs_cstotal); fprintf(dbg_log, "time ufs_time_t %10u\n", (unsigned int)sb->fs_time); fprintf(dbg_log, "size int64_t 0x%08x%08x\n", ((unsigned int *)&(sb->fs_size))[1], ((unsigned int *)&(sb->fs_size))[0]); fprintf(dbg_log, "dsize int64_t 0x%08x%08x\n", ((unsigned int *)&(sb->fs_dsize))[1], ((unsigned int *)&(sb->fs_dsize))[0]); fprintf(dbg_log, "csaddr ufs2_daddr_t 0x%08x%08x\n", ((unsigned int *)&(sb->fs_csaddr))[1], ((unsigned int *)&(sb->fs_csaddr))[0]); fprintf(dbg_log, "pendingblocks int64_t 0x%08x%08x\n", ((unsigned int *)&(sb->fs_pendingblocks))[1], ((unsigned int *)&(sb->fs_pendingblocks))[0]); fprintf(dbg_log, "pendinginodes int32_t 0x%08x\n", sb->fs_pendinginodes); for (j = 0; j < FSMAXSNAP; j++) { fprintf(dbg_log, "snapinum int32_t[%2d] 0x%08x\n", j, sb->fs_snapinum[j]); if (!sb->fs_snapinum[j]) { /* list is dense */ break; } } fprintf(dbg_log, "avgfilesize int32_t 0x%08x\n", sb->fs_avgfilesize); fprintf(dbg_log, "avgfpdir int32_t 0x%08x\n", sb->fs_avgfpdir); fprintf(dbg_log, "save_cgsize int32_t 0x%08x\n", sb->fs_save_cgsize); fprintf(dbg_log, "flags int32_t 0x%08x\n", sb->fs_flags); fprintf(dbg_log, "contigsumsize int32_t 0x%08x\n", sb->fs_contigsumsize); fprintf(dbg_log, "maxsymlinklen int32_t 0x%08x\n", sb->fs_maxsymlinklen); fprintf(dbg_log, "old_inodefmt int32_t 0x%08x\n", sb->fs_old_inodefmt); fprintf(dbg_log, "maxfilesize u_int64_t 0x%08x%08x\n", ((unsigned int *)&(sb->fs_maxfilesize))[1], ((unsigned int *)&(sb->fs_maxfilesize))[0]); fprintf(dbg_log, "qbmask int64_t 0x%08x%08x\n", ((unsigned int *)&(sb->fs_qbmask))[1], ((unsigned int *)&(sb->fs_qbmask))[0]); fprintf(dbg_log, "qfmask int64_t 0x%08x%08x\n", ((unsigned int *)&(sb->fs_qfmask))[1], ((unsigned int *)&(sb->fs_qfmask))[0]); fprintf(dbg_log, "state int32_t 0x%08x\n", sb->fs_state); fprintf(dbg_log, "old_postblformat int32_t 0x%08x\n", sb->fs_old_postblformat); fprintf(dbg_log, "old_nrpos int32_t 0x%08x\n", sb->fs_old_nrpos); fprintf(dbg_log, "spare5 int32_t[2] 0x%08x 0x%08x\n", sb->fs_spare5[0], sb->fs_spare5[1]); fprintf(dbg_log, "magic int32_t 0x%08x\n", sb->fs_magic); indent--; fprintf(dbg_log, "===== END SUPERBLOCK =====\n"); return; } /* * Dump a cylinder group. */ void dbg_dump_cg(const char *comment, struct cg *cgr) { int j; if (!dbg_log) return; fprintf(dbg_log, "===== START CYLINDER GROUP =====\n"); fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)cgr, comment); indent++; fprintf(dbg_log, "magic int32_t 0x%08x\n", cgr->cg_magic); fprintf(dbg_log, "old_time int32_t 0x%08x\n", cgr->cg_old_time); fprintf(dbg_log, "cgx int32_t 0x%08x\n", cgr->cg_cgx); fprintf(dbg_log, "old_ncyl int16_t 0x%04x\n", cgr->cg_old_ncyl); fprintf(dbg_log, "old_niblk int16_t 0x%04x\n", cgr->cg_old_niblk); fprintf(dbg_log, "ndblk int32_t 0x%08x\n", cgr->cg_ndblk); dbg_dump_csum("internal cs", &cgr->cg_cs); fprintf(dbg_log, "rotor int32_t 0x%08x\n", cgr->cg_rotor); fprintf(dbg_log, "frotor int32_t 0x%08x\n", cgr->cg_frotor); fprintf(dbg_log, "irotor int32_t 0x%08x\n", cgr->cg_irotor); for (j = 0; j < MAXFRAG; j++) { fprintf(dbg_log, "frsum int32_t[%d] 0x%08x\n", j, cgr->cg_frsum[j]); } fprintf(dbg_log, "old_btotoff int32_t 0x%08x\n", cgr->cg_old_btotoff); fprintf(dbg_log, "old_boff int32_t 0x%08x\n", cgr->cg_old_boff); fprintf(dbg_log, "iusedoff int32_t 0x%08x\n", cgr->cg_iusedoff); fprintf(dbg_log, "freeoff int32_t 0x%08x\n", cgr->cg_freeoff); fprintf(dbg_log, "nextfreeoff int32_t 0x%08x\n", cgr->cg_nextfreeoff); fprintf(dbg_log, "clustersumoff int32_t 0x%08x\n", cgr->cg_clustersumoff); fprintf(dbg_log, "clusteroff int32_t 0x%08x\n", cgr->cg_clusteroff); fprintf(dbg_log, "nclusterblks int32_t 0x%08x\n", cgr->cg_nclusterblks); fprintf(dbg_log, "niblk int32_t 0x%08x\n", cgr->cg_niblk); fprintf(dbg_log, "initediblk int32_t 0x%08x\n", cgr->cg_initediblk); fprintf(dbg_log, "unrefs int32_t 0x%08x\n", cgr->cg_unrefs); fprintf(dbg_log, "time ufs_time_t %10u\n", (unsigned int)cgr->cg_initediblk); indent--; fprintf(dbg_log, "===== END CYLINDER GROUP =====\n"); return; } /* * Dump a cylinder summary. */ void dbg_dump_csum(const char *comment, struct csum *cs) { if (!dbg_log) return; fprintf(dbg_log, "===== START CYLINDER SUMMARY =====\n"); fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)cs, comment); indent++; fprintf(dbg_log, "ndir int32_t 0x%08x\n", cs->cs_ndir); fprintf(dbg_log, "nbfree int32_t 0x%08x\n", cs->cs_nbfree); fprintf(dbg_log, "nifree int32_t 0x%08x\n", cs->cs_nifree); fprintf(dbg_log, "nffree int32_t 0x%08x\n", cs->cs_nffree); indent--; fprintf(dbg_log, "===== END CYLINDER SUMMARY =====\n"); return; } /* * Dump a cylinder summary. */ void dbg_dump_csum_total(const char *comment, struct csum_total *cs) { if (!dbg_log) return; fprintf(dbg_log, "===== START CYLINDER SUMMARY TOTAL =====\n"); fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)cs, comment); indent++; fprintf(dbg_log, "ndir int64_t 0x%08x%08x\n", ((unsigned int *)&(cs->cs_ndir))[1], ((unsigned int *)&(cs->cs_ndir))[0]); fprintf(dbg_log, "nbfree int64_t 0x%08x%08x\n", ((unsigned int *)&(cs->cs_nbfree))[1], ((unsigned int *)&(cs->cs_nbfree))[0]); fprintf(dbg_log, "nifree int64_t 0x%08x%08x\n", ((unsigned int *)&(cs->cs_nifree))[1], ((unsigned int *)&(cs->cs_nifree))[0]); fprintf(dbg_log, "nffree int64_t 0x%08x%08x\n", ((unsigned int *)&(cs->cs_nffree))[1], ((unsigned int *)&(cs->cs_nffree))[0]); fprintf(dbg_log, "numclusters int64_t 0x%08x%08x\n", ((unsigned int *)&(cs->cs_numclusters))[1], ((unsigned int *)&(cs->cs_numclusters))[0]); indent--; fprintf(dbg_log, "===== END CYLINDER SUMMARY TOTAL =====\n"); return; } /* * Dump the inode allocation map in one cylinder group. */ void dbg_dump_inmap(struct fs *sb, const char *comment, struct cg *cgr) { int j,k,l,e; unsigned char *cp; if (!dbg_log) return; fprintf(dbg_log, "===== START INODE ALLOCATION MAP =====\n"); fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)cgr, comment); indent++; cp = (unsigned char *)cg_inosused(cgr); e = sb->fs_ipg / 8; for (j = 0; j < e; j += 32) { fprintf(dbg_log, "%08x: ", j); for (k = 0; k < 32; k += 8) { if (j + k + 8 < e) { fprintf(dbg_log, "%02x%02x%02x%02x%02x%02x%02x%02x ", cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]); } else { for (l = 0; (l < 8) && (j + k + l < e); l++) { fprintf(dbg_log, "%02x", cp[l]); } } cp += 8; } fprintf(dbg_log, "\n"); } indent--; fprintf(dbg_log, "===== END INODE ALLOCATION MAP =====\n"); return; } /* * Dump the fragment allocation map in one cylinder group. */ void dbg_dump_frmap(struct fs *sb, const char *comment, struct cg *cgr) { int j,k,l,e; unsigned char *cp; if (!dbg_log) return; fprintf(dbg_log, "===== START FRAGMENT ALLOCATION MAP =====\n"); fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)cgr, comment); indent++; cp = (unsigned char *)cg_blksfree(cgr); if (sb->fs_old_nspf) e = howmany(sb->fs_old_cpg * sb->fs_old_spc / sb->fs_old_nspf, CHAR_BIT); else e = 0; for (j = 0; j < e; j += 32) { fprintf(dbg_log, "%08x: ", j); for (k = 0; k < 32; k += 8) { if (j + k + 8 fs_old_nspf) e = howmany(sb->fs_old_cpg * sb->fs_old_spc / (sb->fs_old_nspf << sb->fs_fragshift), CHAR_BIT); else e = 0; for (j = 0; j < e; j += 32) { fprintf(dbg_log, "%08x: ", j); for (k = 0; k < 32; k += 8) { if (j + k + 8 < e) { fprintf(dbg_log, "%02x%02x%02x%02x%02x%02x%02x%02x ", cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]); } else { for (l = 0; (l < 8) && (j + k + l fs_contigsumsize; j++) { fprintf(dbg_log, "%02d: %8d\n", j, *ip++); } indent--; fprintf(dbg_log, "===== END CLUSTER SUMMARY =====\n"); return; } #ifdef NOT_CURRENTLY /* * This code dates from before the UFS2 integration, and doesn't compile * post-UFS2 due to the use of cg_blks(). I'm not sure how best to update * this for UFS2, where the rotational bits of UFS no longer apply, so * will leave it disabled for now; it should probably be re-enabled * specifically for UFS1. */ /* * Dump the block summary, and the rotational layout table. */ void dbg_dump_sptbl(struct fs *sb, const char *comment, struct cg *cgr) { int j,k; int *ip; if (!dbg_log) return; fprintf(dbg_log, "===== START BLOCK SUMMARY AND POSITION TABLE =====\n"); fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)cgr, comment); indent++; ip = (int *)cg_blktot(cgr); for (j = 0; j < sb->fs_old_cpg; j++) { fprintf(dbg_log, "%2d: %5d = ", j, *ip++); for (k = 0; k < sb->fs_old_nrpos; k++) { fprintf(dbg_log, "%4d", cg_blks(sb, cgr, j)[k]); if (k < sb->fs_old_nrpos - 1) fprintf(dbg_log, " + "); } fprintf(dbg_log, "\n"); } indent--; fprintf(dbg_log, "===== END BLOCK SUMMARY AND POSITION TABLE =====\n"); return; } #endif /* * Dump a UFS1 inode structure. */ void dbg_dump_ufs1_ino(struct fs *sb, const char *comment, struct ufs1_dinode *ino) { int ictr; int remaining_blocks; if (!dbg_log) return; fprintf(dbg_log, "===== START UFS1 INODE DUMP =====\n"); fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)ino, comment); indent++; fprintf(dbg_log, "mode u_int16_t 0%o\n", ino->di_mode); fprintf(dbg_log, "nlink int16_t 0x%04x\n", ino->di_nlink); fprintf(dbg_log, "size u_int64_t 0x%08x%08x\n", ((unsigned int *)&(ino->di_size))[1], ((unsigned int *)&(ino->di_size))[0]); fprintf(dbg_log, "atime int32_t 0x%08x\n", ino->di_atime); fprintf(dbg_log, "atimensec int32_t 0x%08x\n", ino->di_atimensec); fprintf(dbg_log, "mtime int32_t 0x%08x\n", ino->di_mtime); fprintf(dbg_log, "mtimensec int32_t 0x%08x\n", ino->di_mtimensec); fprintf(dbg_log, "ctime int32_t 0x%08x\n", ino->di_ctime); fprintf(dbg_log, "ctimensec int32_t 0x%08x\n", ino->di_ctimensec); remaining_blocks = howmany(ino->di_size, sb->fs_bsize); /* XXX ts - +1? */ for (ictr = 0; ictr < MIN(UFS_NDADDR, remaining_blocks); ictr++) { fprintf(dbg_log, "db ufs_daddr_t[%x] 0x%08x\n", ictr, ino->di_db[ictr]); } remaining_blocks -= UFS_NDADDR; if (remaining_blocks > 0) { fprintf(dbg_log, "ib ufs_daddr_t[0] 0x%08x\n", ino->di_ib[0]); } remaining_blocks -= howmany(sb->fs_bsize, sizeof(ufs1_daddr_t)); if (remaining_blocks > 0) { fprintf(dbg_log, "ib ufs_daddr_t[1] 0x%08x\n", ino->di_ib[1]); } #define SQUARE(a) ((a) * (a)) remaining_blocks -= SQUARE(howmany(sb->fs_bsize, sizeof(ufs1_daddr_t))); #undef SQUARE if (remaining_blocks > 0) { fprintf(dbg_log, "ib ufs_daddr_t[2] 0x%08x\n", ino->di_ib[2]); } fprintf(dbg_log, "flags u_int32_t 0x%08x\n", ino->di_flags); fprintf(dbg_log, "blocks int32_t 0x%08x\n", ino->di_blocks); fprintf(dbg_log, "gen int32_t 0x%08x\n", ino->di_gen); fprintf(dbg_log, "uid u_int32_t 0x%08x\n", ino->di_uid); fprintf(dbg_log, "gid u_int32_t 0x%08x\n", ino->di_gid); indent--; fprintf(dbg_log, "===== END UFS1 INODE DUMP =====\n"); return; } /* * Dump a UFS2 inode structure. */ void dbg_dump_ufs2_ino(struct fs *sb, const char *comment, struct ufs2_dinode *ino) { int ictr; int remaining_blocks; if (!dbg_log) return; fprintf(dbg_log, "===== START UFS2 INODE DUMP =====\n"); fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)ino, comment); indent++; fprintf(dbg_log, "mode u_int16_t 0%o\n", ino->di_mode); fprintf(dbg_log, "nlink int16_t 0x%04x\n", ino->di_nlink); fprintf(dbg_log, "uid u_int32_t 0x%08x\n", ino->di_uid); fprintf(dbg_log, "gid u_int32_t 0x%08x\n", ino->di_gid); fprintf(dbg_log, "blksize u_int32_t 0x%08x\n", ino->di_blksize); fprintf(dbg_log, "size u_int64_t 0x%08x%08x\n", ((unsigned int *)&(ino->di_size))[1], ((unsigned int *)&(ino->di_size))[0]); fprintf(dbg_log, "blocks u_int64_t 0x%08x%08x\n", ((unsigned int *)&(ino->di_blocks))[1], ((unsigned int *)&(ino->di_blocks))[0]); fprintf(dbg_log, "atime ufs_time_t %10jd\n", ino->di_atime); fprintf(dbg_log, "mtime ufs_time_t %10jd\n", ino->di_mtime); fprintf(dbg_log, "ctime ufs_time_t %10jd\n", ino->di_ctime); fprintf(dbg_log, "birthtime ufs_time_t %10jd\n", ino->di_birthtime); fprintf(dbg_log, "mtimensec int32_t 0x%08x\n", ino->di_mtimensec); fprintf(dbg_log, "atimensec int32_t 0x%08x\n", ino->di_atimensec); fprintf(dbg_log, "ctimensec int32_t 0x%08x\n", ino->di_ctimensec); fprintf(dbg_log, "birthnsec int32_t 0x%08x\n", ino->di_birthnsec); fprintf(dbg_log, "gen int32_t 0x%08x\n", ino->di_gen); fprintf(dbg_log, "kernflags u_int32_t 0x%08x\n", ino->di_kernflags); fprintf(dbg_log, "flags u_int32_t 0x%08x\n", ino->di_flags); fprintf(dbg_log, "extsize u_int32_t 0x%08x\n", ino->di_extsize); /* XXX: What do we do with di_extb[UFS_NXADDR]? */ remaining_blocks = howmany(ino->di_size, sb->fs_bsize); /* XXX ts - +1? */ for (ictr = 0; ictr < MIN(UFS_NDADDR, remaining_blocks); ictr++) { fprintf(dbg_log, "db ufs2_daddr_t[%x] 0x%16jx\n", ictr, ino->di_db[ictr]); } remaining_blocks -= UFS_NDADDR; if (remaining_blocks > 0) { fprintf(dbg_log, "ib ufs2_daddr_t[0] 0x%16jx\n", ino->di_ib[0]); } remaining_blocks -= howmany(sb->fs_bsize, sizeof(ufs2_daddr_t)); if (remaining_blocks > 0) { fprintf(dbg_log, "ib ufs2_daddr_t[1] 0x%16jx\n", ino->di_ib[1]); } #define SQUARE(a) ((a) * (a)) remaining_blocks -= SQUARE(howmany(sb->fs_bsize, sizeof(ufs2_daddr_t))); #undef SQUARE if (remaining_blocks > 0) { fprintf(dbg_log, "ib ufs2_daddr_t[2] 0x%16jx\n", ino->di_ib[2]); } indent--; fprintf(dbg_log, "===== END UFS2 INODE DUMP =====\n"); return; } /* * Dump an indirect block. The iteration to dump a full file has to be * written around. */ void dbg_dump_iblk(struct fs *sb, const char *comment, char *block, size_t length) { unsigned int *mem, i, j, size; if (!dbg_log) return; fprintf(dbg_log, "===== START INDIRECT BLOCK DUMP =====\n"); fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)block, comment); indent++; if (sb->fs_magic == FS_UFS1_MAGIC) size = sizeof(ufs1_daddr_t); else size = sizeof(ufs2_daddr_t); mem = (unsigned int *)block; for (i = 0; (size_t)i < MIN(howmany(sb->fs_bsize, size), length); i += 8) { fprintf(dbg_log, "%04x: ", i); for (j = 0; j < 8; j++) { if ((size_t)(i + j) < length) fprintf(dbg_log, "%08X ", *mem++); } fprintf(dbg_log, "\n"); } indent--; fprintf(dbg_log, "===== END INDIRECT BLOCK DUMP =====\n"); return; } #endif /* FS_DEBUG */ diff --git a/sbin/ifconfig/af_inet.c b/sbin/ifconfig/af_inet.c index 83b605e8c4cb..d9499d64ed13 100644 --- a/sbin/ifconfig/af_inet.c +++ b/sbin/ifconfig/af_inet.c @@ -1,582 +1,577 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" #include "ifconfig_netlink.h" #ifdef WITHOUT_NETLINK static struct in_aliasreq in_addreq; static struct ifreq in_ridreq; #else struct in_px { struct in_addr addr; int plen; bool addrset; bool maskset; }; struct in_pdata { struct in_px addr; struct in_px dst_addr; struct in_px brd_addr; uint32_t flags; uint32_t vhid; }; static struct in_pdata in_add, in_del; #endif static char addr_buf[NI_MAXHOST]; /*for getnameinfo()*/ extern char *f_inet, *f_addr; static void print_addr(struct sockaddr_in *sin) { int error, n_flags; if (f_addr != NULL && strcmp(f_addr, "fqdn") == 0) n_flags = 0; else if (f_addr != NULL && strcmp(f_addr, "host") == 0) n_flags = NI_NOFQDN; else n_flags = NI_NUMERICHOST; error = getnameinfo((struct sockaddr *)sin, sin->sin_len, addr_buf, sizeof(addr_buf), NULL, 0, n_flags); if (error) inet_ntop(AF_INET, &sin->sin_addr, addr_buf, sizeof(addr_buf)); printf("\tinet %s", addr_buf); } #ifdef WITHOUT_NETLINK static void in_status(if_ctx *ctx __unused, const struct ifaddrs *ifa) { struct sockaddr_in *sin, null_sin = {}; sin = satosin(ifa->ifa_addr); if (sin == NULL) return; print_addr(sin); if (ifa->ifa_flags & IFF_POINTOPOINT) { sin = satosin(ifa->ifa_dstaddr); if (sin == NULL) sin = &null_sin; printf(" --> %s", inet_ntoa(sin->sin_addr)); } sin = satosin(ifa->ifa_netmask); if (sin == NULL) sin = &null_sin; if (f_inet != NULL && strcmp(f_inet, "cidr") == 0) { int cidr = 32; unsigned long smask; smask = ntohl(sin->sin_addr.s_addr); while ((smask & 1) == 0) { smask = smask >> 1; cidr--; if (cidr == 0) break; } printf("/%d", cidr); } else if (f_inet != NULL && strcmp(f_inet, "dotted") == 0) printf(" netmask %s", inet_ntoa(sin->sin_addr)); else printf(" netmask 0x%lx", (unsigned long)ntohl(sin->sin_addr.s_addr)); if (ifa->ifa_flags & IFF_BROADCAST) { sin = satosin(ifa->ifa_broadaddr); if (sin != NULL && sin->sin_addr.s_addr != 0) printf(" broadcast %s", inet_ntoa(sin->sin_addr)); } print_vhid(ifa); putchar('\n'); } #else static struct in_addr get_mask(int plen) { struct in_addr a; a.s_addr = htonl(plen ? ~((1 << (32 - plen)) - 1) : 0); return (a); } static void in_status_nl(if_ctx *ctx __unused, if_link_t *link, if_addr_t *ifa) { struct sockaddr_in *sin = satosin(ifa->ifa_local); int plen = ifa->ifa_prefixlen; print_addr(sin); if (link->ifi_flags & IFF_POINTOPOINT) { struct sockaddr_in *dst = satosin(ifa->ifa_address); printf(" --> %s", inet_ntoa(dst->sin_addr)); } if (f_inet != NULL && strcmp(f_inet, "cidr") == 0) { printf("/%d", plen); } else if (f_inet != NULL && strcmp(f_inet, "dotted") == 0) printf(" netmask %s", inet_ntoa(get_mask(plen))); else printf(" netmask 0x%lx", (unsigned long)ntohl(get_mask(plen).s_addr)); if ((link->ifi_flags & IFF_BROADCAST) && plen != 0) { struct sockaddr_in *brd = satosin(ifa->ifa_broadcast); if (brd != NULL) printf(" broadcast %s", inet_ntoa(brd->sin_addr)); } if (ifa->ifaf_vhid != 0) printf(" vhid %d", ifa->ifaf_vhid); putchar('\n'); } #endif #ifdef WITHOUT_NETLINK #define SIN(x) ((struct sockaddr_in *) &(x)) static struct sockaddr_in *sintab[] = { SIN(in_ridreq.ifr_addr), SIN(in_addreq.ifra_addr), SIN(in_addreq.ifra_mask), SIN(in_addreq.ifra_broadaddr) }; static void in_copyaddr(if_ctx *ctx __unused, int to, int from) { memcpy(sintab[to], sintab[from], sizeof(struct sockaddr_in)); } static void in_getaddr(const char *s, int which) { struct sockaddr_in *sin = sintab[which]; struct hostent *hp; struct netent *np; sin->sin_len = sizeof(*sin); sin->sin_family = AF_INET; if (which == ADDR) { char *p = NULL; if((p = strrchr(s, '/')) != NULL) { const char *errstr; /* address is `name/masklen' */ int masklen = 0; struct sockaddr_in *min = sintab[MASK]; *p = '\0'; if (!isdigit(*(p + 1))) errstr = "invalid"; else masklen = (int)strtonum(p + 1, 0, 32, &errstr); if (errstr != NULL) { *p = '/'; errx(1, "%s: bad value (width %s)", s, errstr); } min->sin_family = AF_INET; min->sin_len = sizeof(*min); min->sin_addr.s_addr = htonl(~((1LL << (32 - masklen)) - 1) & 0xffffffff); } } if (inet_aton(s, &sin->sin_addr)) return; if ((hp = gethostbyname(s)) != NULL) bcopy(hp->h_addr, (char *)&sin->sin_addr, MIN((size_t)hp->h_length, sizeof(sin->sin_addr))); else if ((np = getnetbyname(s)) != NULL) sin->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY); else errx(1, "%s: bad value", s); } #else static struct in_px *sintab_nl[] = { &in_del.addr, /* RIDADDR */ &in_add.addr, /* ADDR */ NULL, /* MASK */ &in_add.dst_addr, /* DSTADDR*/ &in_add.brd_addr, /* BRDADDR*/ }; static void in_copyaddr(if_ctx *ctx __unused, int to, int from) { sintab_nl[to]->addr = sintab_nl[from]->addr; sintab_nl[to]->addrset = sintab_nl[from]->addrset; } static void in_getip(const char *addr_str, struct in_addr *ip) { struct hostent *hp; struct netent *np; if (inet_aton(addr_str, ip)) return; if ((hp = gethostbyname(addr_str)) != NULL) bcopy(hp->h_addr, (char *)ip, MIN((size_t)hp->h_length, sizeof(ip))); else if ((np = getnetbyname(addr_str)) != NULL) *ip = inet_makeaddr(np->n_net, INADDR_ANY); else errx(1, "%s: bad value", addr_str); } static void in_getaddr(const char *s, int which) { struct in_px *px = sintab_nl[which]; if (which == MASK) { struct in_px *px_addr = sintab_nl[ADDR]; struct in_addr mask = {}; in_getip(s, &mask); px_addr->plen = __bitcount32(mask.s_addr); px_addr->maskset = true; return; } if (which == ADDR) { char *p = NULL; if((p = strrchr(s, '/')) != NULL) { const char *errstr; /* address is `name/masklen' */ int masklen; *p = '\0'; if (!isdigit(*(p + 1))) errstr = "invalid"; else masklen = (int)strtonum(p + 1, 0, 32, &errstr); if (errstr != NULL) { *p = '/'; errx(1, "%s: bad value (width %s)", s, errstr); } px->plen = masklen; px->maskset = true; } } in_getip(s, &px->addr); px->addrset = true; } /* * Deletes the first found IPv4 interface address for the interface. * * This function provides SIOCDIFADDR semantics missing in Netlink. * When no valid IPv4 address is specified (sin_family or sin_len is wrong) to * the SIOCDIFADDR call, it deletes the first found IPv4 address on the interface. * 'ifconfig IFNAME inet addr/prefix' relies on that behavior, as it * executes empty SIOCDIFADDR before adding a new address. */ static int in_delete_first_nl(if_ctx *ctx) { struct nlmsghdr *hdr; struct ifaddrmsg *ifahdr; uint32_t nlmsg_seq; struct in_addr addr; struct snl_writer nw = {}; struct snl_errmsg_data e = {}; struct snl_state *ss = ctx->io_ss; bool found = false; uint32_t ifindex = if_nametoindex_nl(ss, ctx->ifname); if (ifindex == 0) { /* No interface with the desired name, nothing to delete */ return (EADDRNOTAVAIL); } snl_init_writer(ss, &nw); hdr = snl_create_msg_request(&nw, NL_RTM_GETADDR); hdr->nlmsg_flags |= NLM_F_DUMP; ifahdr = snl_reserve_msg_object(&nw, struct ifaddrmsg); ifahdr->ifa_family = AF_INET; ifahdr->ifa_index = ifindex; if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) return (EINVAL); nlmsg_seq = hdr->nlmsg_seq; while ((hdr = snl_read_reply_multi(ss, nlmsg_seq, &e)) != NULL) { struct snl_parsed_addr attrs = {}; if (snl_parse_nlmsg(ss, hdr, &snl_rtm_addr_parser, &attrs)) { addr = satosin(attrs.ifa_local)->sin_addr; ifindex = attrs.ifa_index; found = true; break; } else return (EINVAL); } if (e.error != 0) { if (e.error_str != NULL) warnx("%s(): %s", __func__, e.error_str); return (e.error); } if (!found) return (0); /* Try to delete the found address */ snl_init_writer(ss, &nw); hdr = snl_create_msg_request(&nw, NL_RTM_DELADDR); ifahdr = snl_reserve_msg_object(&nw, struct ifaddrmsg); ifahdr->ifa_family = AF_INET; ifahdr->ifa_index = ifindex; snl_add_msg_attr_ip4(&nw, IFA_LOCAL, &addr); if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) return (EINVAL); memset(&e, 0, sizeof(e)); snl_read_reply_code(ss, hdr->nlmsg_seq, &e); if (e.error_str != NULL) warnx("%s(): %s", __func__, e.error_str); return (e.error); } static int in_exec_nl(if_ctx *ctx, unsigned long action, void *data) { struct in_pdata *pdata = (struct in_pdata *)data; struct snl_writer nw = {}; if (action == NL_RTM_DELADDR && !pdata->addr.addrset) return (in_delete_first_nl(ctx)); snl_init_writer(ctx->io_ss, &nw); struct nlmsghdr *hdr = snl_create_msg_request(&nw, action); struct ifaddrmsg *ifahdr = snl_reserve_msg_object(&nw, struct ifaddrmsg); ifahdr->ifa_family = AF_INET; ifahdr->ifa_prefixlen = pdata->addr.plen; ifahdr->ifa_index = if_nametoindex_nl(ctx->io_ss, ctx->ifname); snl_add_msg_attr_ip4(&nw, IFA_LOCAL, &pdata->addr.addr); if (action == NL_RTM_NEWADDR && pdata->dst_addr.addrset) snl_add_msg_attr_ip4(&nw, IFA_ADDRESS, &pdata->dst_addr.addr); if (action == NL_RTM_NEWADDR && pdata->brd_addr.addrset) snl_add_msg_attr_ip4(&nw, IFA_BROADCAST, &pdata->brd_addr.addr); int off = snl_add_msg_attr_nested(&nw, IFA_FREEBSD); snl_add_msg_attr_u32(&nw, IFAF_FLAGS, pdata->flags); if (pdata->vhid != 0) snl_add_msg_attr_u32(&nw, IFAF_VHID, pdata->vhid); snl_end_attr_nested(&nw, off); if (!snl_finalize_msg(&nw) || !snl_send_message(ctx->io_ss, hdr)) return (0); struct snl_errmsg_data e = {}; snl_read_reply_code(ctx->io_ss, hdr->nlmsg_seq, &e); if (e.error_str != NULL) warnx("%s(): %s", __func__, e.error_str); return (e.error); } static void in_setdefaultmask_nl(void) { struct in_px *px = sintab_nl[ADDR]; in_addr_t i = ntohl(px->addr.s_addr); /* * If netmask isn't supplied, use historical default. * This is deprecated for interfaces other than loopback * or point-to-point; warn in other cases. In the future * we should return an error rather than warning. */ if (IN_CLASSA(i)) px->plen = IN_CLASSA_NSHIFT; else if (IN_CLASSB(i)) px->plen = IN_CLASSB_NSHIFT; else px->plen = IN_CLASSC_NSHIFT; px->maskset = true; } #endif static void warn_nomask(int ifflags) { if ((ifflags & (IFF_POINTOPOINT | IFF_LOOPBACK)) == 0) { warnx("WARNING: setting interface address without mask " "is deprecated,\ndefault mask may not be correct."); } } static void in_postproc(if_ctx *ctx __unused, int newaddr, int ifflags) { #ifdef WITHOUT_NETLINK if (sintab[ADDR]->sin_len != 0 && sintab[MASK]->sin_len == 0 && newaddr) { warn_nomask(ifflags); } #else if (sintab_nl[ADDR]->addrset && !sintab_nl[ADDR]->maskset && newaddr) { warn_nomask(ifflags); in_setdefaultmask_nl(); } #endif } static void in_status_tunnel(if_ctx *ctx) { char src[NI_MAXHOST]; char dst[NI_MAXHOST]; struct ifreq ifr; const struct sockaddr *sa = (const struct sockaddr *) &ifr.ifr_addr; memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, ctx->ifname, IFNAMSIZ); if (ioctl_ctx(ctx, SIOCGIFPSRCADDR, (caddr_t)&ifr) < 0) return; if (sa->sa_family != AF_INET) return; if (getnameinfo(sa, sa->sa_len, src, sizeof(src), 0, 0, NI_NUMERICHOST) != 0) src[0] = '\0'; if (ioctl_ctx(ctx, SIOCGIFPDSTADDR, (caddr_t)&ifr) < 0) return; if (sa->sa_family != AF_INET) return; if (getnameinfo(sa, sa->sa_len, dst, sizeof(dst), 0, 0, NI_NUMERICHOST) != 0) dst[0] = '\0'; printf("\ttunnel inet %s --> %s\n", src, dst); } static void in_set_tunnel(if_ctx *ctx, struct addrinfo *srcres, struct addrinfo *dstres) { struct in_aliasreq addreq; memset(&addreq, 0, sizeof(addreq)); strlcpy(addreq.ifra_name, ctx->ifname, IFNAMSIZ); memcpy(&addreq.ifra_addr, srcres->ai_addr, srcres->ai_addr->sa_len); memcpy(&addreq.ifra_dstaddr, dstres->ai_addr, dstres->ai_addr->sa_len); if (ioctl_ctx(ctx, SIOCSIFPHYADDR, &addreq) < 0) warn("SIOCSIFPHYADDR"); } static void in_set_vhid(int vhid) { #ifdef WITHOUT_NETLINK in_addreq.ifra_vhid = vhid; #else in_add.vhid = (uint32_t)vhid; #endif } static struct afswtch af_inet = { .af_name = "inet", .af_af = AF_INET, #ifdef WITHOUT_NETLINK .af_status = in_status, #else .af_status = in_status_nl, #endif .af_getaddr = in_getaddr, .af_copyaddr = in_copyaddr, .af_postproc = in_postproc, .af_status_tunnel = in_status_tunnel, .af_settunnel = in_set_tunnel, .af_setvhid = in_set_vhid, #ifdef WITHOUT_NETLINK .af_difaddr = SIOCDIFADDR, .af_aifaddr = SIOCAIFADDR, .af_ridreq = &in_ridreq, .af_addreq = &in_addreq, .af_exec = af_exec_ioctl, #else .af_difaddr = NL_RTM_DELADDR, .af_aifaddr = NL_RTM_NEWADDR, .af_ridreq = &in_del, .af_addreq = &in_add, .af_exec = in_exec_nl, #endif }; static __constructor void inet_ctor(void) { #ifndef RESCUE if (!feature_present("inet")) return; #endif af_register(&af_inet); } diff --git a/sbin/ifconfig/af_inet6.c b/sbin/ifconfig/af_inet6.c index a0138e9b3de6..bc4f77f6848d 100644 --- a/sbin/ifconfig/af_inet6.c +++ b/sbin/ifconfig/af_inet6.c @@ -1,793 +1,788 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Define ND6_INFINITE_LIFETIME */ #include "ifconfig.h" #include "ifconfig_netlink.h" #ifndef WITHOUT_NETLINK struct in6_px { struct in6_addr addr; int plen; bool set; }; struct in6_pdata { struct in6_px addr; struct in6_px dst_addr; struct in6_addrlifetime lifetime; uint32_t flags; uint32_t vhid; }; static struct in6_pdata in6_del; static struct in6_pdata in6_add = { .lifetime = { 0, 0, ND6_INFINITE_LIFETIME, ND6_INFINITE_LIFETIME }, }; #else static struct in6_ifreq in6_ridreq; static struct in6_aliasreq in6_addreq = { .ifra_flags = 0, .ifra_lifetime = { 0, 0, ND6_INFINITE_LIFETIME, ND6_INFINITE_LIFETIME } }; #endif static int ip6lifetime; #ifdef WITHOUT_NETLINK static int prefix(void *, int); #endif static char *sec2str(time_t); static int explicit_prefix = 0; extern char *f_inet6, *f_addr; extern void setnd6flags(if_ctx *, const char *, int); extern void setnd6defif(if_ctx *,const char *, int); extern void nd6_status(if_ctx *); static char addr_buf[NI_MAXHOST]; /*for getnameinfo()*/ static void setifprefixlen(if_ctx *ctx __netlink_unused, const char *addr, int dummy __unused) { #ifdef WITHOUT_NETLINK const struct afswtch *afp = ctx->afp; if (afp->af_getprefix != NULL) afp->af_getprefix(addr, MASK); #else int plen = strtol(addr, NULL, 10); if ((plen < 0) || (plen > 128)) errx(1, "%s: bad value", addr); in6_add.addr.plen = plen; #endif explicit_prefix = 1; } static void setip6flags(if_ctx *ctx, const char *dummyaddr __unused, int flag) { const struct afswtch *afp = ctx->afp; if (afp->af_af != AF_INET6) err(1, "address flags can be set only for inet6 addresses"); #ifdef WITHOUT_NETLINK if (flag < 0) in6_addreq.ifra_flags &= ~(-flag); else in6_addreq.ifra_flags |= flag; #else if (flag < 0) in6_add.flags &= ~(-flag); else in6_add.flags |= flag; #endif } static void setip6lifetime(if_ctx *ctx, const char *cmd, const char *val) { const struct afswtch *afp = ctx->afp; struct timespec now; time_t newval; char *ep; #ifdef WITHOUT_NETLINK struct in6_addrlifetime *lifetime = &in6_addreq.ifra_lifetime; #else struct in6_addrlifetime *lifetime = &in6_add.lifetime; #endif clock_gettime(CLOCK_MONOTONIC_FAST, &now); newval = (time_t)strtoul(val, &ep, 0); if (val == ep) errx(1, "invalid %s", cmd); if (afp->af_af != AF_INET6) errx(1, "%s not allowed for the AF", cmd); if (strcmp(cmd, "vltime") == 0) { lifetime->ia6t_expire = now.tv_sec + newval; lifetime->ia6t_vltime = newval; } else if (strcmp(cmd, "pltime") == 0) { lifetime->ia6t_preferred = now.tv_sec + newval; lifetime->ia6t_pltime = newval; } } static void setip6pltime(if_ctx *ctx, const char *seconds, int dummy __unused) { setip6lifetime(ctx, "pltime", seconds); } static void setip6vltime(if_ctx *ctx, const char *seconds, int dummy __unused) { setip6lifetime(ctx, "vltime", seconds); } static void setip6eui64(if_ctx *ctx, const char *cmd, int dummy __unused) { const struct afswtch *afp = ctx->afp; struct ifaddrs *ifap, *ifa; const struct sockaddr_in6 *sin6 = NULL; const struct in6_addr *lladdr = NULL; struct in6_addr *in6; if (afp->af_af != AF_INET6) errx(EXIT_FAILURE, "%s not allowed for the AF", cmd); #ifdef WITHOUT_NETLINK in6 = (struct in6_addr *)&in6_addreq.ifra_addr.sin6_addr; #else in6 = &in6_add.addr.addr; #endif if (memcmp(&in6addr_any.s6_addr[8], &in6->s6_addr[8], 8) != 0) errx(EXIT_FAILURE, "interface index is already filled"); if (getifaddrs(&ifap) != 0) err(EXIT_FAILURE, "getifaddrs"); for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr->sa_family == AF_INET6 && strcmp(ifa->ifa_name, ctx->ifname) == 0) { sin6 = (const struct sockaddr_in6 *)satosin6(ifa->ifa_addr); if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { lladdr = &sin6->sin6_addr; break; } } } if (!lladdr) errx(EXIT_FAILURE, "could not determine link local address"); memcpy(&in6->s6_addr[8], &lladdr->s6_addr[8], 8); freeifaddrs(ifap); } static void print_addr(struct sockaddr_in6 *sin) { int error, n_flags; if (f_addr != NULL && strcmp(f_addr, "fqdn") == 0) n_flags = 0; else if (f_addr != NULL && strcmp(f_addr, "host") == 0) n_flags = NI_NOFQDN; else n_flags = NI_NUMERICHOST; error = getnameinfo((struct sockaddr *)sin, sin->sin6_len, addr_buf, sizeof(addr_buf), NULL, 0, n_flags); if (error != 0) inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf, sizeof(addr_buf)); printf("\tinet6 %s", addr_buf); } static void print_p2p(struct sockaddr_in6 *sin) { int error; error = getnameinfo((struct sockaddr *)sin, sin->sin6_len, addr_buf, sizeof(addr_buf), NULL, 0, NI_NUMERICHOST); if (error != 0) inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf, sizeof(addr_buf)); printf(" --> %s", addr_buf); } static void print_mask(int plen) { if (f_inet6 != NULL && strcmp(f_inet6, "cidr") == 0) printf("/%d", plen); else printf(" prefixlen %d", plen); } static void print_flags(int flags6) { if ((flags6 & IN6_IFF_ANYCAST) != 0) printf(" anycast"); if ((flags6 & IN6_IFF_TENTATIVE) != 0) printf(" tentative"); if ((flags6 & IN6_IFF_DUPLICATED) != 0) printf(" duplicated"); if ((flags6 & IN6_IFF_DETACHED) != 0) printf(" detached"); if ((flags6 & IN6_IFF_DEPRECATED) != 0) printf(" deprecated"); if ((flags6 & IN6_IFF_AUTOCONF) != 0) printf(" autoconf"); if ((flags6 & IN6_IFF_TEMPORARY) != 0) printf(" temporary"); if ((flags6 & IN6_IFF_PREFER_SOURCE) != 0) printf(" prefer_source"); } static void print_lifetime(const char *prepend, time_t px_time, struct timespec *now) { printf(" %s", prepend); if (px_time == 0) printf(" infty"); printf(" %s", px_time < now->tv_sec ? "0" : sec2str(px_time - now->tv_sec)); } #ifdef WITHOUT_NETLINK static void in6_status(if_ctx *ctx, const struct ifaddrs *ifa) { struct sockaddr_in6 *sin, null_sin = {}; struct in6_ifreq ifr6; int s6; u_int32_t flags6; struct in6_addrlifetime lifetime; sin = satosin6(ifa->ifa_addr); if (sin == NULL) return; strlcpy(ifr6.ifr_name, ctx->ifname, sizeof(ifr6.ifr_name)); if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { warn("socket(AF_INET6,SOCK_DGRAM)"); return; } ifr6.ifr_addr = *sin; if (ioctl(s6, SIOCGIFAFLAG_IN6, &ifr6) < 0) { warn("ioctl(SIOCGIFAFLAG_IN6)"); close(s6); return; } flags6 = ifr6.ifr_ifru.ifru_flags6; memset(&lifetime, 0, sizeof(lifetime)); ifr6.ifr_addr = *sin; if (ioctl(s6, SIOCGIFALIFETIME_IN6, &ifr6) < 0) { warn("ioctl(SIOCGIFALIFETIME_IN6)"); close(s6); return; } lifetime = ifr6.ifr_ifru.ifru_lifetime; close(s6); print_addr(sin); if (ifa->ifa_flags & IFF_POINTOPOINT) { sin = satosin6(ifa->ifa_dstaddr); /* * some of the interfaces do not have valid destination * address. */ if (sin != NULL && sin->sin6_family == AF_INET6) print_p2p(sin); } sin = satosin6(ifa->ifa_netmask); if (sin == NULL) sin = &null_sin; print_mask(prefix(&sin->sin6_addr, sizeof(struct in6_addr))); print_flags(flags6); if ((satosin6(ifa->ifa_addr))->sin6_scope_id) printf(" scopeid 0x%x", (satosin6(ifa->ifa_addr))->sin6_scope_id); if (ip6lifetime && (lifetime.ia6t_preferred || lifetime.ia6t_expire)) { struct timespec now; clock_gettime(CLOCK_MONOTONIC_FAST, &now); print_lifetime("pltime", lifetime.ia6t_preferred, &now); print_lifetime("vltime", lifetime.ia6t_expire, &now); } print_vhid(ifa); putchar('\n'); } #else static void show_lifetime(struct ifa_cacheinfo *ci) { struct timespec now; uint32_t pl, vl; if (ci == NULL) return; int count = ci->ifa_prefered != ND6_INFINITE_LIFETIME; count += ci->ifa_valid != ND6_INFINITE_LIFETIME; if (count == 0) return; pl = (ci->ifa_prefered == ND6_INFINITE_LIFETIME) ? 0 : ci->ifa_prefered; vl = (ci->ifa_valid == ND6_INFINITE_LIFETIME) ? 0 : ci->ifa_valid; clock_gettime(CLOCK_MONOTONIC_FAST, &now); print_lifetime("pltime", pl + now.tv_sec, &now); print_lifetime("vltime", vl + now.tv_sec, &now); } static void in6_status_nl(if_ctx *ctx __unused, if_link_t *link __unused, if_addr_t *ifa) { int plen = ifa->ifa_prefixlen; uint32_t scopeid; if (ifa->ifa_local == NULL) { /* Non-P2P address */ scopeid = satosin6(ifa->ifa_address)->sin6_scope_id; print_addr(satosin6(ifa->ifa_address)); } else { scopeid = satosin6(ifa->ifa_local)->sin6_scope_id; print_addr(satosin6(ifa->ifa_local)); print_p2p(satosin6(ifa->ifa_address)); } print_mask(plen); print_flags(ifa->ifaf_flags); if (scopeid != 0) printf(" scopeid 0x%x", scopeid); show_lifetime(ifa->ifa_cacheinfo); if (ifa->ifaf_vhid != 0) printf(" vhid %d", ifa->ifaf_vhid); putchar('\n'); } static struct in6_px *sin6tab_nl[] = { &in6_del.addr, /* RIDADDR */ &in6_add.addr, /* ADDR */ NULL, /* MASK */ &in6_add.dst_addr, /* DSTADDR*/ }; static void in6_copyaddr(if_ctx *ctx __unused, int to, int from) { sin6tab_nl[to]->addr = sin6tab_nl[from]->addr; sin6tab_nl[to]->set = sin6tab_nl[from]->set; } static void in6_getaddr(const char *addr_str, int which) { struct in6_px *px = sin6tab_nl[which]; px->set = true; px->plen = 128; if (which == ADDR) { char *p = NULL; if((p = strrchr(addr_str, '/')) != NULL) { *p = '\0'; int plen = strtol(p + 1, NULL, 10); if (plen < 0 || plen > 128) errx(1, "%s: bad value", p + 1); px->plen = plen; explicit_prefix = 1; } } struct addrinfo hints = { .ai_family = AF_INET6 }; struct addrinfo *res; int error = getaddrinfo(addr_str, NULL, &hints, &res); if (error != 0) { if (inet_pton(AF_INET6, addr_str, &px->addr) != 1) errx(1, "%s: bad value", addr_str); } else { struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *)(void *)res->ai_addr; px->addr = sin6->sin6_addr; freeaddrinfo(res); } } static int in6_exec_nl(if_ctx *ctx, unsigned long action, void *data) { struct in6_pdata *pdata = (struct in6_pdata *)data; struct snl_writer nw = {}; snl_init_writer(ctx->io_ss, &nw); struct nlmsghdr *hdr = snl_create_msg_request(&nw, action); struct ifaddrmsg *ifahdr = snl_reserve_msg_object(&nw, struct ifaddrmsg); ifahdr->ifa_family = AF_INET6; ifahdr->ifa_prefixlen = pdata->addr.plen; ifahdr->ifa_index = if_nametoindex_nl(ctx->io_ss, ctx->ifname); snl_add_msg_attr_ip6(&nw, IFA_LOCAL, &pdata->addr.addr); if (action == NL_RTM_NEWADDR && pdata->dst_addr.set) snl_add_msg_attr_ip6(&nw, IFA_ADDRESS, &pdata->dst_addr.addr); struct ifa_cacheinfo ci = { .ifa_prefered = pdata->lifetime.ia6t_pltime, .ifa_valid = pdata->lifetime.ia6t_vltime, }; snl_add_msg_attr(&nw, IFA_CACHEINFO, sizeof(ci), &ci); int off = snl_add_msg_attr_nested(&nw, IFA_FREEBSD); snl_add_msg_attr_u32(&nw, IFAF_FLAGS, pdata->flags); if (pdata->vhid != 0) snl_add_msg_attr_u32(&nw, IFAF_VHID, pdata->vhid); snl_end_attr_nested(&nw, off); if (!snl_finalize_msg(&nw) || !snl_send_message(ctx->io_ss, hdr)) return (0); struct snl_errmsg_data e = {}; snl_read_reply_code(ctx->io_ss, hdr->nlmsg_seq, &e); return (e.error); } #endif #ifdef WITHOUT_NETLINK static struct sockaddr_in6 *sin6tab[] = { &in6_ridreq.ifr_addr, &in6_addreq.ifra_addr, &in6_addreq.ifra_prefixmask, &in6_addreq.ifra_dstaddr }; static void in6_copyaddr(if_ctx *ctx __unused, int to, int from) { memcpy(sin6tab[to], sin6tab[from], sizeof(struct sockaddr_in6)); } static void in6_getprefix(const char *plen, int which) { struct sockaddr_in6 *sin = sin6tab[which]; u_char *cp; int len = atoi(plen); if ((len < 0) || (len > 128)) errx(1, "%s: bad value", plen); sin->sin6_len = sizeof(*sin); if (which != MASK) sin->sin6_family = AF_INET6; if ((len == 0) || (len == 128)) { memset(&sin->sin6_addr, 0xff, sizeof(struct in6_addr)); return; } memset((void *)&sin->sin6_addr, 0x00, sizeof(sin->sin6_addr)); for (cp = (u_char *)&sin->sin6_addr; len > 7; len -= 8) *cp++ = 0xff; *cp = 0xff << (8 - len); } static void in6_getaddr(const char *s, int which) { struct sockaddr_in6 *sin = sin6tab[which]; struct addrinfo hints, *res; int error = -1; sin->sin6_len = sizeof(*sin); if (which != MASK) sin->sin6_family = AF_INET6; if (which == ADDR) { char *p = NULL; if((p = strrchr(s, '/')) != NULL) { *p = '\0'; in6_getprefix(p + 1, MASK); explicit_prefix = 1; } } if (sin->sin6_family == AF_INET6) { bzero(&hints, sizeof(struct addrinfo)); hints.ai_family = AF_INET6; error = getaddrinfo(s, NULL, &hints, &res); if (error != 0) { if (inet_pton(AF_INET6, s, &sin->sin6_addr) != 1) errx(1, "%s: bad value", s); } else { bcopy(res->ai_addr, sin, res->ai_addrlen); freeaddrinfo(res); } } } static int prefix(void *val, int size) { u_char *name = (u_char *)val; int byte, bit, plen = 0; for (byte = 0; byte < size; byte++, plen += 8) if (name[byte] != 0xff) break; if (byte == size) return (plen); for (bit = 7; bit != 0; bit--, plen++) if (!(name[byte] & (1 << bit))) break; for (; bit != 0; bit--) if (name[byte] & (1 << bit)) return(0); byte++; for (; byte < size; byte++) if (name[byte]) return(0); return (plen); } #endif static char * sec2str(time_t total) { static char result[256]; int days, hours, mins, secs; int first = 1; char *p = result; if (0) { days = total / 3600 / 24; hours = (total / 3600) % 24; mins = (total / 60) % 60; secs = total % 60; if (days) { first = 0; p += sprintf(p, "%dd", days); } if (!first || hours) { first = 0; p += sprintf(p, "%dh", hours); } if (!first || mins) { first = 0; p += sprintf(p, "%dm", mins); } sprintf(p, "%ds", secs); } else sprintf(result, "%lu", (unsigned long)total); return(result); } static void in6_postproc(if_ctx *ctx, int newaddr __unused, int ifflags __unused) { if (explicit_prefix == 0) { /* Aggregatable address architecture defines all prefixes are 64. So, it is convenient to set prefixlen to 64 if it is not specified. */ setifprefixlen(ctx, "64", 0); /* in6_getprefix("64", MASK) if MASK is available here... */ } } static void in6_status_tunnel(if_ctx *ctx) { char src[NI_MAXHOST]; char dst[NI_MAXHOST]; struct in6_ifreq in6_ifr; const struct sockaddr *sa = (const struct sockaddr *) &in6_ifr.ifr_addr; memset(&in6_ifr, 0, sizeof(in6_ifr)); strlcpy(in6_ifr.ifr_name, ctx->ifname, sizeof(in6_ifr.ifr_name)); if (ioctl_ctx(ctx, SIOCGIFPSRCADDR_IN6, (caddr_t)&in6_ifr) < 0) return; if (sa->sa_family != AF_INET6) return; if (getnameinfo(sa, sa->sa_len, src, sizeof(src), 0, 0, NI_NUMERICHOST) != 0) src[0] = '\0'; if (ioctl_ctx(ctx, SIOCGIFPDSTADDR_IN6, (caddr_t)&in6_ifr) < 0) return; if (sa->sa_family != AF_INET6) return; if (getnameinfo(sa, sa->sa_len, dst, sizeof(dst), 0, 0, NI_NUMERICHOST) != 0) dst[0] = '\0'; printf("\ttunnel inet6 %s --> %s\n", src, dst); } static void in6_set_tunnel(if_ctx *ctx, struct addrinfo *srcres, struct addrinfo *dstres) { struct in6_aliasreq in6_req = {}; strlcpy(in6_req.ifra_name, ctx->ifname, sizeof(in6_req.ifra_name)); memcpy(&in6_req.ifra_addr, srcres->ai_addr, srcres->ai_addr->sa_len); memcpy(&in6_req.ifra_dstaddr, dstres->ai_addr, dstres->ai_addr->sa_len); if (ioctl_ctx(ctx, SIOCSIFPHYADDR_IN6, &in6_req) < 0) warn("SIOCSIFPHYADDR_IN6"); } static void in6_set_vhid(int vhid) { #ifdef WITHOUT_NETLINK in6_addreq.ifra_vhid = vhid; #else in6_add.vhid = (uint32_t)vhid; #endif } static struct cmd inet6_cmds[] = { DEF_CMD_ARG("prefixlen", setifprefixlen), DEF_CMD("anycast", IN6_IFF_ANYCAST, setip6flags), DEF_CMD("tentative", IN6_IFF_TENTATIVE, setip6flags), DEF_CMD("-tentative", -IN6_IFF_TENTATIVE, setip6flags), DEF_CMD("deprecated", IN6_IFF_DEPRECATED, setip6flags), DEF_CMD("-deprecated", -IN6_IFF_DEPRECATED, setip6flags), DEF_CMD("autoconf", IN6_IFF_AUTOCONF, setip6flags), DEF_CMD("-autoconf", -IN6_IFF_AUTOCONF, setip6flags), DEF_CMD("prefer_source",IN6_IFF_PREFER_SOURCE, setip6flags), DEF_CMD("-prefer_source",-IN6_IFF_PREFER_SOURCE,setip6flags), DEF_CMD("accept_rtadv", ND6_IFF_ACCEPT_RTADV, setnd6flags), DEF_CMD("-accept_rtadv",-ND6_IFF_ACCEPT_RTADV, setnd6flags), DEF_CMD("no_radr", ND6_IFF_NO_RADR, setnd6flags), DEF_CMD("-no_radr", -ND6_IFF_NO_RADR, setnd6flags), DEF_CMD("defaultif", 1, setnd6defif), DEF_CMD("-defaultif", -1, setnd6defif), DEF_CMD("ifdisabled", ND6_IFF_IFDISABLED, setnd6flags), DEF_CMD("-ifdisabled", -ND6_IFF_IFDISABLED, setnd6flags), DEF_CMD("nud", ND6_IFF_PERFORMNUD, setnd6flags), DEF_CMD("-nud", -ND6_IFF_PERFORMNUD, setnd6flags), DEF_CMD("auto_linklocal",ND6_IFF_AUTO_LINKLOCAL,setnd6flags), DEF_CMD("-auto_linklocal",-ND6_IFF_AUTO_LINKLOCAL,setnd6flags), DEF_CMD("no_prefer_iface",ND6_IFF_NO_PREFER_IFACE,setnd6flags), DEF_CMD("-no_prefer_iface",-ND6_IFF_NO_PREFER_IFACE,setnd6flags), DEF_CMD("no_dad", ND6_IFF_NO_DAD, setnd6flags), DEF_CMD("-no_dad", -ND6_IFF_NO_DAD, setnd6flags), DEF_CMD_ARG("pltime", setip6pltime), DEF_CMD_ARG("vltime", setip6vltime), DEF_CMD("eui64", 0, setip6eui64), #ifdef EXPERIMENTAL DEF_CMD("ipv6_only", ND6_IFF_IPV6_ONLY_MANUAL,setnd6flags), DEF_CMD("-ipv6_only", -ND6_IFF_IPV6_ONLY_MANUAL,setnd6flags), #endif }; static struct afswtch af_inet6 = { .af_name = "inet6", .af_af = AF_INET6, #ifdef WITHOUT_NETLINK .af_status = in6_status, #else .af_status = in6_status_nl, #endif .af_getaddr = in6_getaddr, .af_copyaddr = in6_copyaddr, #ifdef WITHOUT_NETLINK .af_getprefix = in6_getprefix, #endif .af_other_status = nd6_status, .af_postproc = in6_postproc, .af_status_tunnel = in6_status_tunnel, .af_settunnel = in6_set_tunnel, .af_setvhid = in6_set_vhid, #ifdef WITHOUT_NETLINK .af_difaddr = SIOCDIFADDR_IN6, .af_aifaddr = SIOCAIFADDR_IN6, .af_ridreq = &in6_addreq, .af_addreq = &in6_addreq, .af_exec = af_exec_ioctl, #else .af_difaddr = NL_RTM_DELADDR, .af_aifaddr = NL_RTM_NEWADDR, .af_ridreq = &in6_add, .af_addreq = &in6_add, .af_exec = in6_exec_nl, #endif }; static void in6_Lopt_cb(const char *arg __unused) { ip6lifetime++; /* print IPv6 address lifetime */ } static struct option in6_Lopt = { .opt = "L", .opt_usage = "[-L]", .cb = in6_Lopt_cb }; static __constructor void inet6_ctor(void) { size_t i; #ifndef RESCUE if (!feature_present("inet6")) return; #endif for (i = 0; i < nitems(inet6_cmds); i++) cmd_register(&inet6_cmds[i]); af_register(&af_inet6); opt_register(&in6_Lopt); } diff --git a/sbin/ifconfig/af_link.c b/sbin/ifconfig/af_link.c index 2a53daeb7d01..ab35d04a8709 100644 --- a/sbin/ifconfig/af_link.c +++ b/sbin/ifconfig/af_link.c @@ -1,274 +1,269 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" #include "ifconfig_netlink.h" static struct ifreq link_ridreq; extern char *f_ether; static void print_ether(const struct ether_addr *addr, const char *prefix) { char *ether_format = ether_ntoa(addr); if (f_ether != NULL) { if (strcmp(f_ether, "dash") == 0) { char *format_char; while ((format_char = strchr(ether_format, ':')) != NULL) { *format_char = '-'; } } else if (strcmp(f_ether, "dotted") == 0) { /* Indices 0 and 1 is kept as is. */ ether_format[ 2] = ether_format[ 3]; ether_format[ 3] = ether_format[ 4]; ether_format[ 4] = '.'; ether_format[ 5] = ether_format[ 6]; ether_format[ 6] = ether_format[ 7]; ether_format[ 7] = ether_format[ 9]; ether_format[ 8] = ether_format[10]; ether_format[ 9] = '.'; ether_format[10] = ether_format[12]; ether_format[11] = ether_format[13]; ether_format[12] = ether_format[15]; ether_format[13] = ether_format[16]; ether_format[14] = '\0'; } } printf("\t%s %s\n", prefix, ether_format); } static void print_lladdr(struct sockaddr_dl *sdl) { if (match_ether(sdl)) { print_ether((struct ether_addr *)LLADDR(sdl), "ether"); } else { int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0; printf("\tlladdr %s\n", link_ntoa(sdl) + n); } } static void print_pcp(if_ctx *ctx) { struct ifreq ifr = {}; if (ioctl_ctx_ifr(ctx, SIOCGLANPCP, &ifr) == 0 && ifr.ifr_lan_pcp != IFNET_PCP_NONE) printf("\tpcp %d\n", ifr.ifr_lan_pcp); } #ifdef WITHOUT_NETLINK static void link_status(if_ctx *ctx, const struct ifaddrs *ifa) { /* XXX no const 'cuz LLADDR is defined wrong */ struct sockaddr_dl *sdl; struct ifreq ifr; int rc, sock_hw; static const u_char laggaddr[6] = {0}; sdl = satosdl(ifa->ifa_addr); if (sdl == NULL || sdl->sdl_alen == 0) return; print_lladdr(sdl); /* * Best-effort (i.e. failures are silent) to get original * hardware address, as read by NIC driver at attach time. Only * applies to Ethernet NICs (IFT_ETHER). However, laggX * interfaces claim to be IFT_ETHER, and re-type their component * Ethernet NICs as IFT_IEEE8023ADLAG. So, check for both. If * the MAC is zeroed, then it's actually a lagg. */ if ((sdl->sdl_type != IFT_ETHER && sdl->sdl_type != IFT_IEEE8023ADLAG) || sdl->sdl_alen != ETHER_ADDR_LEN) return; strncpy(ifr.ifr_name, ifa->ifa_name, sizeof(ifr.ifr_name)); memcpy(&ifr.ifr_addr, ifa->ifa_addr, sizeof(ifa->ifa_addr->sa_len)); ifr.ifr_addr.sa_family = AF_LOCAL; if ((sock_hw = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0) { warn("socket(AF_LOCAL,SOCK_DGRAM)"); return; } rc = ioctl(sock_hw, SIOCGHWADDR, &ifr); close(sock_hw); if (rc != 0) return; /* * If this is definitely a lagg device or the hwaddr * matches the link addr, don't bother. */ if (memcmp(ifr.ifr_addr.sa_data, laggaddr, sdl->sdl_alen) == 0 || memcmp(ifr.ifr_addr.sa_data, LLADDR(sdl), sdl->sdl_alen) == 0) goto pcp; print_ether((const struct ether_addr *)&ifr.ifr_addr.sa_data, "hwaddr"); pcp: print_pcp(ctx); } #else static void link_status_nl(if_ctx *ctx, if_link_t *link, if_addr_t *ifa __unused) { if (link->ifla_address != NULL) { struct sockaddr_dl sdl = { .sdl_len = sizeof(struct sockaddr_dl), .sdl_family = AF_LINK, .sdl_type = convert_iftype(link->ifi_type), .sdl_alen = NLA_DATA_LEN(link->ifla_address), }; memcpy(LLADDR(&sdl), NLA_DATA(link->ifla_address), sdl.sdl_alen); print_lladdr(&sdl); if (link->iflaf_orig_hwaddr != NULL) { struct nlattr *hwaddr = link->iflaf_orig_hwaddr; if (memcmp(NLA_DATA(hwaddr), NLA_DATA(link->ifla_address), sdl.sdl_alen)) print_ether((struct ether_addr *)NLA_DATA(hwaddr), "hwaddr"); } } if (convert_iftype(link->ifi_type) == IFT_ETHER) print_pcp(ctx); } #endif static void link_getaddr(const char *addr, int which) { char *temp; struct sockaddr_dl sdl; struct sockaddr *sa = &link_ridreq.ifr_addr; if (which != ADDR) errx(1, "can't set link-level netmask or broadcast"); if (!strcmp(addr, "random")) { sdl.sdl_len = sizeof(sdl); sdl.sdl_alen = ETHER_ADDR_LEN; sdl.sdl_nlen = 0; sdl.sdl_family = AF_LINK; arc4random_buf(&sdl.sdl_data, ETHER_ADDR_LEN); /* Non-multicast and claim it is locally administered. */ sdl.sdl_data[0] &= 0xfc; sdl.sdl_data[0] |= 0x02; } else { if ((temp = malloc(strlen(addr) + 2)) == NULL) errx(1, "malloc failed"); temp[0] = ':'; strcpy(temp + 1, addr); sdl.sdl_len = sizeof(sdl); link_addr(temp, &sdl); free(temp); } if (sdl.sdl_alen > sizeof(sa->sa_data)) errx(1, "malformed link-level address"); sa->sa_family = AF_LINK; sa->sa_len = sdl.sdl_alen; bcopy(LLADDR(&sdl), sa->sa_data, sdl.sdl_alen); } static struct afswtch af_link = { .af_name = "link", .af_af = AF_LINK, #ifdef WITHOUT_NETLINK .af_status = link_status, #else .af_status = link_status_nl, #endif .af_getaddr = link_getaddr, .af_aifaddr = SIOCSIFLLADDR, .af_addreq = &link_ridreq, .af_exec = af_exec_ioctl, }; static struct afswtch af_ether = { .af_name = "ether", .af_af = AF_LINK, #ifdef WITHOUT_NETLINK .af_status = link_status, #else .af_status = link_status_nl, #endif .af_getaddr = link_getaddr, .af_aifaddr = SIOCSIFLLADDR, .af_addreq = &link_ridreq, .af_exec = af_exec_ioctl, }; static struct afswtch af_lladdr = { .af_name = "lladdr", .af_af = AF_LINK, #ifdef WITHOUT_NETLINK .af_status = link_status, #else .af_status = link_status_nl, #endif .af_getaddr = link_getaddr, .af_aifaddr = SIOCSIFLLADDR, .af_addreq = &link_ridreq, .af_exec = af_exec_ioctl, }; static __constructor void link_ctor(void) { af_register(&af_link); af_register(&af_ether); af_register(&af_lladdr); } diff --git a/sbin/ifconfig/af_nd6.c b/sbin/ifconfig/af_nd6.c index 7eeb86585197..73044e95740a 100644 --- a/sbin/ifconfig/af_nd6.c +++ b/sbin/ifconfig/af_nd6.c @@ -1,170 +1,165 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009 Hiroki Sato. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" #define MAX_SYSCTL_TRY 5 #ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG #define ND6BITS "\020\001PERFORMNUD\002ACCEPT_RTADV\003PREFER_SOURCE" \ "\004IFDISABLED\005DONT_SET_IFROUTE\006AUTO_LINKLOCAL" \ "\007NO_RADR\010NO_PREFER_IFACE\011NO_DAD" \ "\012IPV6_ONLY\013IPV6_ONLY_MANUAL" \ "\020DEFAULTIF" #else #define ND6BITS "\020\001PERFORMNUD\002ACCEPT_RTADV\003PREFER_SOURCE" \ "\004IFDISABLED\005DONT_SET_IFROUTE\006AUTO_LINKLOCAL" \ "\007NO_RADR\010NO_PREFER_IFACE\011NO_DAD\020DEFAULTIF" #endif static int isnd6defif(if_ctx *ctx, int s); void setnd6flags(if_ctx *, const char *, int); void setnd6defif(if_ctx *,const char *, int); void nd6_status(if_ctx *); void setnd6flags(if_ctx *ctx, const char *dummyaddr __unused, int d) { struct in6_ndireq nd = {}; int error; strlcpy(nd.ifname, ctx->ifname, sizeof(nd.ifname)); error = ioctl_ctx(ctx, SIOCGIFINFO_IN6, &nd); if (error) { warn("ioctl(SIOCGIFINFO_IN6)"); return; } if (d < 0) nd.ndi.flags &= ~(-d); else nd.ndi.flags |= d; error = ioctl_ctx(ctx, SIOCSIFINFO_IN6, (caddr_t)&nd); if (error) warn("ioctl(SIOCSIFINFO_IN6)"); } void setnd6defif(if_ctx *ctx, const char *dummyaddr __unused, int d) { struct in6_ndifreq ndifreq = {}; int ifindex; int error; strlcpy(ndifreq.ifname, ctx->ifname, sizeof(ndifreq.ifname)); if (d < 0) { if (isnd6defif(ctx, ctx->io_s)) { /* ifindex = 0 means to remove default if */ ifindex = 0; } else return; } else if ((ifindex = if_nametoindex(ndifreq.ifname)) == 0) { warn("if_nametoindex(%s)", ndifreq.ifname); return; } ndifreq.ifindex = ifindex; error = ioctl_ctx(ctx, SIOCSDEFIFACE_IN6, (caddr_t)&ndifreq); if (error) warn("ioctl(SIOCSDEFIFACE_IN6)"); } static int isnd6defif(if_ctx *ctx, int s) { struct in6_ndifreq ndifreq = {}; unsigned int ifindex; int error; strlcpy(ndifreq.ifname, ctx->ifname, sizeof(ndifreq.ifname)); ifindex = if_nametoindex(ndifreq.ifname); error = ioctl(s, SIOCGDEFIFACE_IN6, (caddr_t)&ndifreq); if (error) { warn("ioctl(SIOCGDEFIFACE_IN6)"); return (error); } return (ndifreq.ifindex == ifindex); } void nd6_status(if_ctx *ctx) { struct in6_ndireq nd = {}; int s6; int error; int isdefif; strlcpy(nd.ifname, ctx->ifname, sizeof(nd.ifname)); if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { if (errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT) warn("socket(AF_INET6, SOCK_DGRAM)"); return; } error = ioctl(s6, SIOCGIFINFO_IN6, &nd); if (error) { if (errno != EPFNOSUPPORT) warn("ioctl(SIOCGIFINFO_IN6)"); close(s6); return; } isdefif = isnd6defif(ctx, s6); close(s6); if (nd.ndi.flags == 0 && !isdefif) return; printb("\tnd6 options", (unsigned int)(nd.ndi.flags | (isdefif << 15)), ND6BITS); putchar('\n'); } diff --git a/sbin/ifconfig/ifbridge.c b/sbin/ifconfig/ifbridge.c index 3a97a5af3931..9b499b404946 100644 --- a/sbin/ifconfig/ifbridge.c +++ b/sbin/ifconfig/ifbridge.c @@ -1,683 +1,678 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright 2001 Wasabi Systems, Inc. * All rights reserved. * * Written by Jason R. Thorpe for Wasabi Systems, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed for the NetBSD Project by * Wasabi Systems, Inc. * 4. The name of Wasabi Systems, Inc. may not be used to endorse * or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" static const char *stpstates[] = { STP_STATES }; static const char *stpproto[] = { STP_PROTOS }; static const char *stproles[] = { STP_ROLES }; static int get_val(const char *cp, u_long *valp) { char *endptr; u_long val; errno = 0; val = strtoul(cp, &endptr, 0); if (cp[0] == '\0' || endptr[0] != '\0' || errno == ERANGE) return (-1); *valp = val; return (0); } static int do_cmd(if_ctx *ctx, u_long op, void *arg, size_t argsize, int set) { struct ifdrv ifd = {}; strlcpy(ifd.ifd_name, ctx->ifname, sizeof(ifd.ifd_name)); ifd.ifd_cmd = op; ifd.ifd_len = argsize; ifd.ifd_data = arg; return (ioctl_ctx(ctx, set ? SIOCSDRVSPEC : SIOCGDRVSPEC, &ifd)); } static void do_bridgeflag(if_ctx *ctx, const char *ifs, int flag, int set) { struct ifbreq req; strlcpy(req.ifbr_ifsname, ifs, sizeof(req.ifbr_ifsname)); if (do_cmd(ctx, BRDGGIFFLGS, &req, sizeof(req), 0) < 0) err(1, "unable to get bridge flags"); if (set) req.ifbr_ifsflags |= flag; else req.ifbr_ifsflags &= ~flag; if (do_cmd(ctx, BRDGSIFFLGS, &req, sizeof(req), 1) < 0) err(1, "unable to set bridge flags"); } static void bridge_addresses(if_ctx *ctx, const char *prefix) { struct ifbaconf ifbac; struct ifbareq *ifba; char *inbuf = NULL, *ninbuf; size_t len = 8192; struct ether_addr ea; for (;;) { ninbuf = realloc(inbuf, len); if (ninbuf == NULL) err(1, "unable to allocate address buffer"); ifbac.ifbac_len = len; ifbac.ifbac_buf = inbuf = ninbuf; if (do_cmd(ctx, BRDGRTS, &ifbac, sizeof(ifbac), 0) < 0) err(1, "unable to get address cache"); if ((ifbac.ifbac_len + sizeof(*ifba)) < len) break; len *= 2; } for (unsigned long i = 0; i < ifbac.ifbac_len / sizeof(*ifba); i++) { ifba = ifbac.ifbac_req + i; memcpy(ea.octet, ifba->ifba_dst, sizeof(ea.octet)); printf("%s%s Vlan%d %s %lu ", prefix, ether_ntoa(&ea), ifba->ifba_vlan, ifba->ifba_ifsname, ifba->ifba_expire); printb("flags", ifba->ifba_flags, IFBAFBITS); printf("\n"); } free(inbuf); } static void bridge_status(if_ctx *ctx) { struct ifconfig_bridge_status *bridge; struct ifbropreq *params; const char *pad, *prefix; uint8_t lladdr[ETHER_ADDR_LEN]; uint16_t bprio; if (ifconfig_bridge_get_bridge_status(lifh, ctx->ifname, &bridge) == -1) return; params = bridge->params; PV2ID(params->ifbop_bridgeid, bprio, lladdr); printf("\tid %s priority %u hellotime %u fwddelay %u\n", ether_ntoa((struct ether_addr *)lladdr), params->ifbop_priority, params->ifbop_hellotime, params->ifbop_fwddelay); printf("\tmaxage %u holdcnt %u proto %s maxaddr %u timeout %u\n", params->ifbop_maxage, params->ifbop_holdcount, stpproto[params->ifbop_protocol], bridge->cache_size, bridge->cache_lifetime); PV2ID(params->ifbop_designated_root, bprio, lladdr); printf("\troot id %s priority %d ifcost %u port %u\n", ether_ntoa((struct ether_addr *)lladdr), bprio, params->ifbop_root_path_cost, params->ifbop_root_port & 0xfff); prefix = "\tmember: "; pad = "\t "; for (size_t i = 0; i < bridge->members_count; ++i) { struct ifbreq *member = &bridge->members[i]; printf("%s%s ", prefix, member->ifbr_ifsname); printb("flags", member->ifbr_ifsflags, IFBIFBITS); printf("\n%s", pad); printf("ifmaxaddr %u port %u priority %u path cost %u", member->ifbr_addrmax, member->ifbr_portno, member->ifbr_priority, member->ifbr_path_cost); if (member->ifbr_ifsflags & IFBIF_STP) { uint8_t proto = member->ifbr_proto; uint8_t role = member->ifbr_role; uint8_t state = member->ifbr_state; if (proto < nitems(stpproto)) printf(" proto %s", stpproto[proto]); else printf(" ", proto); printf("\n%s", pad); if (role < nitems(stproles)) printf("role %s", stproles[role]); else printf("", role); if (state < nitems(stpstates)) printf(" state %s", stpstates[state]); else printf(" ", state); } printf("\n"); } ifconfig_bridge_free_bridge_status(bridge); } static void setbridge_add(if_ctx *ctx, const char *val, int dummy __unused) { struct ifbreq req; memset(&req, 0, sizeof(req)); strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); if (do_cmd(ctx, BRDGADD, &req, sizeof(req), 1) < 0) err(1, "BRDGADD %s", val); } static void setbridge_delete(if_ctx *ctx, const char *val, int dummy __unused) { struct ifbreq req; memset(&req, 0, sizeof(req)); strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); if (do_cmd(ctx, BRDGDEL, &req, sizeof(req), 1) < 0) err(1, "BRDGDEL %s", val); } static void setbridge_discover(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_DISCOVER, 1); } static void unsetbridge_discover(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_DISCOVER, 0); } static void setbridge_learn(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_LEARNING, 1); } static void unsetbridge_learn(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_LEARNING, 0); } static void setbridge_sticky(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_STICKY, 1); } static void unsetbridge_sticky(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_STICKY, 0); } static void setbridge_span(if_ctx *ctx, const char *val, int dummy __unused) { struct ifbreq req; memset(&req, 0, sizeof(req)); strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); if (do_cmd(ctx, BRDGADDS, &req, sizeof(req), 1) < 0) err(1, "BRDGADDS %s", val); } static void unsetbridge_span(if_ctx *ctx, const char *val, int dummy __unused) { struct ifbreq req; memset(&req, 0, sizeof(req)); strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); if (do_cmd(ctx, BRDGDELS, &req, sizeof(req), 1) < 0) err(1, "BRDGDELS %s", val); } static void setbridge_stp(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_STP, 1); } static void unsetbridge_stp(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_STP, 0); } static void setbridge_edge(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_BSTP_EDGE, 1); } static void unsetbridge_edge(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_BSTP_EDGE, 0); } static void setbridge_autoedge(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_BSTP_AUTOEDGE, 1); } static void unsetbridge_autoedge(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_BSTP_AUTOEDGE, 0); } static void setbridge_ptp(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_BSTP_PTP, 1); } static void unsetbridge_ptp(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_BSTP_PTP, 0); } static void setbridge_autoptp(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_BSTP_AUTOPTP, 1); } static void unsetbridge_autoptp(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_BSTP_AUTOPTP, 0); } static void setbridge_flush(if_ctx *ctx, const char *val __unused, int dummy __unused) { struct ifbreq req; memset(&req, 0, sizeof(req)); req.ifbr_ifsflags = IFBF_FLUSHDYN; if (do_cmd(ctx, BRDGFLUSH, &req, sizeof(req), 1) < 0) err(1, "BRDGFLUSH"); } static void setbridge_flushall(if_ctx *ctx, const char *val __unused, int dummy __unused) { struct ifbreq req; memset(&req, 0, sizeof(req)); req.ifbr_ifsflags = IFBF_FLUSHALL; if (do_cmd(ctx, BRDGFLUSH, &req, sizeof(req), 1) < 0) err(1, "BRDGFLUSH"); } static void setbridge_static(if_ctx *ctx, const char *val, const char *mac) { struct ifbareq req; struct ether_addr *ea; memset(&req, 0, sizeof(req)); strlcpy(req.ifba_ifsname, val, sizeof(req.ifba_ifsname)); ea = ether_aton(mac); if (ea == NULL) errx(1, "%s: invalid address: %s", val, mac); memcpy(req.ifba_dst, ea->octet, sizeof(req.ifba_dst)); req.ifba_flags = IFBAF_STATIC; req.ifba_vlan = 1; /* XXX allow user to specify */ if (do_cmd(ctx, BRDGSADDR, &req, sizeof(req), 1) < 0) err(1, "BRDGSADDR %s", val); } static void setbridge_deladdr(if_ctx *ctx, const char *val, int dummy __unused) { struct ifbareq req; struct ether_addr *ea; memset(&req, 0, sizeof(req)); ea = ether_aton(val); if (ea == NULL) errx(1, "invalid address: %s", val); memcpy(req.ifba_dst, ea->octet, sizeof(req.ifba_dst)); if (do_cmd(ctx, BRDGDADDR, &req, sizeof(req), 1) < 0) err(1, "BRDGDADDR %s", val); } static void setbridge_addr(if_ctx *ctx, const char *val __unused, int dummy __unused) { bridge_addresses(ctx, ""); } static void setbridge_maxaddr(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifbrparam param; u_long val; if (get_val(arg, &val) < 0 || (val & ~0xffffffff) != 0) errx(1, "invalid value: %s", arg); param.ifbrp_csize = val & 0xffffffff; if (do_cmd(ctx, BRDGSCACHE, ¶m, sizeof(param), 1) < 0) err(1, "BRDGSCACHE %s", arg); } static void setbridge_hellotime(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifbrparam param; u_long val; if (get_val(arg, &val) < 0 || (val & ~0xff) != 0) errx(1, "invalid value: %s", arg); param.ifbrp_hellotime = val & 0xff; if (do_cmd(ctx, BRDGSHT, ¶m, sizeof(param), 1) < 0) err(1, "BRDGSHT %s", arg); } static void setbridge_fwddelay(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifbrparam param; u_long val; if (get_val(arg, &val) < 0 || (val & ~0xff) != 0) errx(1, "invalid value: %s", arg); param.ifbrp_fwddelay = val & 0xff; if (do_cmd(ctx, BRDGSFD, ¶m, sizeof(param), 1) < 0) err(1, "BRDGSFD %s", arg); } static void setbridge_maxage(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifbrparam param; u_long val; if (get_val(arg, &val) < 0 || (val & ~0xff) != 0) errx(1, "invalid value: %s", arg); param.ifbrp_maxage = val & 0xff; if (do_cmd(ctx, BRDGSMA, ¶m, sizeof(param), 1) < 0) err(1, "BRDGSMA %s", arg); } static void setbridge_priority(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifbrparam param; u_long val; if (get_val(arg, &val) < 0 || (val & ~0xffff) != 0) errx(1, "invalid value: %s", arg); param.ifbrp_prio = val & 0xffff; if (do_cmd(ctx, BRDGSPRI, ¶m, sizeof(param), 1) < 0) err(1, "BRDGSPRI %s", arg); } static void setbridge_protocol(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifbrparam param; if (strcasecmp(arg, "stp") == 0) { param.ifbrp_proto = 0; } else if (strcasecmp(arg, "rstp") == 0) { param.ifbrp_proto = 2; } else { errx(1, "unknown stp protocol"); } if (do_cmd(ctx, BRDGSPROTO, ¶m, sizeof(param), 1) < 0) err(1, "BRDGSPROTO %s", arg); } static void setbridge_holdcount(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifbrparam param; u_long val; if (get_val(arg, &val) < 0 || (val & ~0xff) != 0) errx(1, "invalid value: %s", arg); param.ifbrp_txhc = val & 0xff; if (do_cmd(ctx, BRDGSTXHC, ¶m, sizeof(param), 1) < 0) err(1, "BRDGSTXHC %s", arg); } static void setbridge_ifpriority(if_ctx *ctx, const char *ifn, const char *pri) { struct ifbreq req; u_long val; memset(&req, 0, sizeof(req)); if (get_val(pri, &val) < 0 || (val & ~0xff) != 0) errx(1, "invalid value: %s", pri); strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname)); req.ifbr_priority = val & 0xff; if (do_cmd(ctx, BRDGSIFPRIO, &req, sizeof(req), 1) < 0) err(1, "BRDGSIFPRIO %s", pri); } static void setbridge_ifpathcost(if_ctx *ctx, const char *ifn, const char *cost) { struct ifbreq req; u_long val; memset(&req, 0, sizeof(req)); if (get_val(cost, &val) < 0) errx(1, "invalid value: %s", cost); strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname)); req.ifbr_path_cost = val; if (do_cmd(ctx, BRDGSIFCOST, &req, sizeof(req), 1) < 0) err(1, "BRDGSIFCOST %s", cost); } static void setbridge_ifmaxaddr(if_ctx *ctx, const char *ifn, const char *arg) { struct ifbreq req; u_long val; memset(&req, 0, sizeof(req)); if (get_val(arg, &val) < 0 || (val & ~0xffffffff) != 0) errx(1, "invalid value: %s", arg); strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname)); req.ifbr_addrmax = val & 0xffffffff; if (do_cmd(ctx, BRDGSIFAMAX, &req, sizeof(req), 1) < 0) err(1, "BRDGSIFAMAX %s", arg); } static void setbridge_timeout(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifbrparam param; u_long val; if (get_val(arg, &val) < 0 || (val & ~0xffffffff) != 0) errx(1, "invalid value: %s", arg); param.ifbrp_ctime = val & 0xffffffff; if (do_cmd(ctx, BRDGSTO, ¶m, sizeof(param), 1) < 0) err(1, "BRDGSTO %s", arg); } static void setbridge_private(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_PRIVATE, 1); } static void unsetbridge_private(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_PRIVATE, 0); } static struct cmd bridge_cmds[] = { DEF_CMD_ARG("addm", setbridge_add), DEF_CMD_ARG("deletem", setbridge_delete), DEF_CMD_ARG("discover", setbridge_discover), DEF_CMD_ARG("-discover", unsetbridge_discover), DEF_CMD_ARG("learn", setbridge_learn), DEF_CMD_ARG("-learn", unsetbridge_learn), DEF_CMD_ARG("sticky", setbridge_sticky), DEF_CMD_ARG("-sticky", unsetbridge_sticky), DEF_CMD_ARG("span", setbridge_span), DEF_CMD_ARG("-span", unsetbridge_span), DEF_CMD_ARG("stp", setbridge_stp), DEF_CMD_ARG("-stp", unsetbridge_stp), DEF_CMD_ARG("edge", setbridge_edge), DEF_CMD_ARG("-edge", unsetbridge_edge), DEF_CMD_ARG("autoedge", setbridge_autoedge), DEF_CMD_ARG("-autoedge", unsetbridge_autoedge), DEF_CMD_ARG("ptp", setbridge_ptp), DEF_CMD_ARG("-ptp", unsetbridge_ptp), DEF_CMD_ARG("autoptp", setbridge_autoptp), DEF_CMD_ARG("-autoptp", unsetbridge_autoptp), DEF_CMD("flush", 0, setbridge_flush), DEF_CMD("flushall", 0, setbridge_flushall), DEF_CMD_ARG2("static", setbridge_static), DEF_CMD_ARG("deladdr", setbridge_deladdr), DEF_CMD("addr", 1, setbridge_addr), DEF_CMD_ARG("maxaddr", setbridge_maxaddr), DEF_CMD_ARG("hellotime", setbridge_hellotime), DEF_CMD_ARG("fwddelay", setbridge_fwddelay), DEF_CMD_ARG("maxage", setbridge_maxage), DEF_CMD_ARG("priority", setbridge_priority), DEF_CMD_ARG("proto", setbridge_protocol), DEF_CMD_ARG("holdcnt", setbridge_holdcount), DEF_CMD_ARG2("ifpriority", setbridge_ifpriority), DEF_CMD_ARG2("ifpathcost", setbridge_ifpathcost), DEF_CMD_ARG2("ifmaxaddr", setbridge_ifmaxaddr), DEF_CMD_ARG("timeout", setbridge_timeout), DEF_CMD_ARG("private", setbridge_private), DEF_CMD_ARG("-private", unsetbridge_private), }; static struct afswtch af_bridge = { .af_name = "af_bridge", .af_af = AF_UNSPEC, .af_other_status = bridge_status, }; static __constructor void bridge_ctor(void) { for (size_t i = 0; i < nitems(bridge_cmds); i++) cmd_register(&bridge_cmds[i]); af_register(&af_bridge); } diff --git a/sbin/ifconfig/ifclone.c b/sbin/ifconfig/ifclone.c index 8b378cbe341f..f44d052c97ad 100644 --- a/sbin/ifconfig/ifclone.c +++ b/sbin/ifconfig/ifclone.c @@ -1,191 +1,186 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" typedef enum { MT_PREFIX, MT_FILTER, } clone_match_type; static void list_cloners(void) { char *cloners; size_t cloners_count; if (ifconfig_list_cloners(lifh, &cloners, &cloners_count) < 0) errc(1, ifconfig_err_errno(lifh), "unable to list cloners"); for (const char *name = cloners; name < cloners + cloners_count * IFNAMSIZ; name += IFNAMSIZ) { if (name > cloners) putchar(' '); printf("%s", name); } putchar('\n'); free(cloners); } struct clone_defcb { union { char ifprefix[IFNAMSIZ]; clone_match_func *ifmatch; }; clone_match_type clone_mt; clone_callback_func *clone_cb; SLIST_ENTRY(clone_defcb) next; }; static SLIST_HEAD(, clone_defcb) clone_defcbh = SLIST_HEAD_INITIALIZER(clone_defcbh); void clone_setdefcallback_prefix(const char *ifprefix, clone_callback_func *p) { struct clone_defcb *dcp; dcp = malloc(sizeof(*dcp)); strlcpy(dcp->ifprefix, ifprefix, IFNAMSIZ-1); dcp->clone_mt = MT_PREFIX; dcp->clone_cb = p; SLIST_INSERT_HEAD(&clone_defcbh, dcp, next); } void clone_setdefcallback_filter(clone_match_func *filter, clone_callback_func *p) { struct clone_defcb *dcp; dcp = malloc(sizeof(*dcp)); dcp->ifmatch = filter; dcp->clone_mt = MT_FILTER; dcp->clone_cb = p; SLIST_INSERT_HEAD(&clone_defcbh, dcp, next); } /* * Do the actual clone operation. Any parameters must have been * setup by now. If a callback has been setup to do the work * then defer to it; otherwise do a simple create operation with * no parameters. */ static void ifclonecreate(if_ctx *ctx, void *arg __unused) { struct ifreq ifr = {}; struct clone_defcb *dcp; strlcpy(ifr.ifr_name, ctx->ifname, sizeof(ifr.ifr_name)); /* Try to find a default callback by filter */ SLIST_FOREACH(dcp, &clone_defcbh, next) { if (dcp->clone_mt == MT_FILTER && dcp->ifmatch(ifr.ifr_name) != 0) break; } if (dcp == NULL) { /* Try to find a default callback by prefix */ SLIST_FOREACH(dcp, &clone_defcbh, next) { if (dcp->clone_mt == MT_PREFIX && strncmp(dcp->ifprefix, ifr.ifr_name, strlen(dcp->ifprefix)) == 0) break; } } if (dcp == NULL || dcp->clone_cb == NULL) { /* NB: no parameters */ ifcreate_ioctl(ctx, &ifr); } else { dcp->clone_cb(ctx, &ifr); } } static void clone_create(if_ctx *ctx __unused, const char *cmd __unused, int d __unused) { callback_register(ifclonecreate, NULL); } static void clone_destroy(if_ctx *ctx, const char *cmd __unused, int d __unused) { struct ifreq ifr = {}; if (ioctl_ctx_ifr(ctx, SIOCIFDESTROY, &ifr) < 0) err(1, "SIOCIFDESTROY"); } static struct cmd clone_cmds[] = { DEF_CLONE_CMD("create", 0, clone_create), DEF_CMD("destroy", 0, clone_destroy), DEF_CLONE_CMD("plumb", 0, clone_create), DEF_CMD("unplumb", 0, clone_destroy), }; static void clone_Copt_cb(const char *arg __unused) { list_cloners(); exit(exit_code); } static struct option clone_Copt = { .opt = "C", .opt_usage = "[-C]", .cb = clone_Copt_cb }; static __constructor void clone_ctor(void) { size_t i; for (i = 0; i < nitems(clone_cmds); i++) cmd_register(&clone_cmds[i]); opt_register(&clone_Copt); } diff --git a/sbin/ifconfig/ifconfig.c b/sbin/ifconfig/ifconfig.c index 1a7e5d07e601..4e2f8e782de9 100644 --- a/sbin/ifconfig/ifconfig.c +++ b/sbin/ifconfig/ifconfig.c @@ -1,2107 +1,2105 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1983, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #if 0 static char sccsid[] = "@(#)ifconfig.c 8.2 (Berkeley) 2/16/94"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #ifdef JAIL #include #endif #include #include #include #include #include #include #include #include #include #include #include #include /* IP */ #include #include #include #include #include #include #include #include #include #include #ifdef JAIL #include #endif #include #include #include #include #include #include #include "ifconfig.h" ifconfig_handle_t *lifh; #ifdef WITHOUT_NETLINK static char *descr = NULL; static size_t descrlen = 64; #endif static int setaddr; static int setmask; static int doalias; static int clearaddr; static int newaddr = 1; int exit_code = 0; static char ifname_to_print[IFNAMSIZ]; /* Helper for printifnamemaybe() */ /* Formatter Strings */ char *f_inet, *f_inet6, *f_ether, *f_addr; #ifdef WITHOUT_NETLINK static void list_interfaces_ioctl(if_ctx *ctx); static void status(if_ctx *ctx, const struct sockaddr_dl *sdl, struct ifaddrs *ifa); #endif static _Noreturn void usage(void); static void Perrorc(const char *cmd, int error); static int getifflags(const char *ifname, int us, bool err_ok); static struct afswtch *af_getbyname(const char *name); static struct option *opts = NULL; struct ifa_order_elt { int if_order; int af_orders[255]; struct ifaddrs *ifa; TAILQ_ENTRY(ifa_order_elt) link; }; TAILQ_HEAD(ifa_queue, ifa_order_elt); static struct module_map_entry { const char *ifname; const char *kldname; } module_map[] = { { .ifname = "tun", .kldname = "if_tuntap", }, { .ifname = "tap", .kldname = "if_tuntap", }, { .ifname = "vmnet", .kldname = "if_tuntap", }, { .ifname = "ipsec", .kldname = "ipsec", }, { /* * This mapping exists because there is a conflicting enc module * in CAM. ifconfig's guessing behavior will attempt to match * the ifname to a module as well as if_${ifname} and clash with * CAM enc. This is an assertion of the correct module to load. */ .ifname = "enc", .kldname = "if_enc", }, }; void opt_register(struct option *p) { p->next = opts; opts = p; } static void usage(void) { char options[1024]; struct option *p; /* XXX not right but close enough for now */ options[0] = '\0'; for (p = opts; p != NULL; p = p->next) { strlcat(options, p->opt_usage, sizeof(options)); strlcat(options, " ", sizeof(options)); } fprintf(stderr, "usage: ifconfig [-j jail] [-f type:format] %sinterface address_family\n" " [address [dest_address]] [parameters]\n" " ifconfig [-j jail] interface create\n" " ifconfig [-j jail] -a %s[-d] [-m] [-u] [-v] [address_family]\n" " ifconfig [-j jail] -l [-d] [-u] [address_family]\n" " ifconfig [-j jail] %s[-d] [-m] [-u] [-v]\n", options, options, options); exit(1); } static void ifname_update(if_ctx *ctx, const char *name) { strlcpy(ctx->_ifname_storage_ioctl, name, sizeof(ctx->_ifname_storage_ioctl)); ctx->ifname = ctx->_ifname_storage_ioctl; strlcpy(ifname_to_print, name, sizeof(ifname_to_print)); } static void ifr_set_name(struct ifreq *ifr, const char *name) { strlcpy(ifr->ifr_name, name, sizeof(ifr->ifr_name)); } int ioctl_ctx_ifr(if_ctx *ctx, unsigned long cmd, struct ifreq *ifr) { ifr_set_name(ifr, ctx->ifname); return (ioctl_ctx(ctx, cmd, ifr)); } void ifcreate_ioctl(if_ctx *ctx, struct ifreq *ifr) { char ifname_orig[IFNAMSIZ]; strlcpy(ifname_orig, ifr->ifr_name, sizeof(ifname_orig)); if (ioctl(ctx->io_s, SIOCIFCREATE2, ifr) < 0) { switch (errno) { case EEXIST: errx(1, "interface %s already exists", ifr->ifr_name); default: err(1, "SIOCIFCREATE2 (%s)", ifr->ifr_name); } } if (strncmp(ifname_orig, ifr->ifr_name, sizeof(ifname_orig)) != 0) ifname_update(ctx, ifr->ifr_name); } #ifdef WITHOUT_NETLINK static int calcorders(struct ifaddrs *ifa, struct ifa_queue *q) { struct ifaddrs *prev; struct ifa_order_elt *cur; unsigned int ord, af, ifa_ord; prev = NULL; cur = NULL; ord = 0; ifa_ord = 0; while (ifa != NULL) { if (prev == NULL || strcmp(ifa->ifa_name, prev->ifa_name) != 0) { cur = calloc(1, sizeof(*cur)); if (cur == NULL) return (-1); TAILQ_INSERT_TAIL(q, cur, link); cur->if_order = ifa_ord ++; cur->ifa = ifa; ord = 0; } if (ifa->ifa_addr) { af = ifa->ifa_addr->sa_family; if (af < nitems(cur->af_orders) && cur->af_orders[af] == 0) cur->af_orders[af] = ++ord; } prev = ifa; ifa = ifa->ifa_next; } return (0); } static int cmpifaddrs(struct ifaddrs *a, struct ifaddrs *b, struct ifa_queue *q) { struct ifa_order_elt *cur, *e1, *e2; unsigned int af1, af2; int ret; e1 = e2 = NULL; ret = strcmp(a->ifa_name, b->ifa_name); if (ret != 0) { TAILQ_FOREACH(cur, q, link) { if (e1 && e2) break; if (strcmp(cur->ifa->ifa_name, a->ifa_name) == 0) e1 = cur; else if (strcmp(cur->ifa->ifa_name, b->ifa_name) == 0) e2 = cur; } if (!e1 || !e2) return (0); else return (e1->if_order - e2->if_order); } else if (a->ifa_addr != NULL && b->ifa_addr != NULL) { TAILQ_FOREACH(cur, q, link) { if (strcmp(cur->ifa->ifa_name, a->ifa_name) == 0) { e1 = cur; break; } } if (!e1) return (0); af1 = a->ifa_addr->sa_family; af2 = b->ifa_addr->sa_family; if (af1 < nitems(e1->af_orders) && af2 < nitems(e1->af_orders)) return (e1->af_orders[af1] - e1->af_orders[af2]); } return (0); } #endif static void freeformat(void) { if (f_inet != NULL) free(f_inet); if (f_inet6 != NULL) free(f_inet6); if (f_ether != NULL) free(f_ether); if (f_addr != NULL) free(f_addr); } static void setformat(char *input) { char *formatstr, *category, *modifier; formatstr = strdup(input); while ((category = strsep(&formatstr, ",")) != NULL) { modifier = strchr(category, ':'); if (modifier == NULL || modifier[1] == '\0') { warnx("Skipping invalid format specification: %s\n", category); continue; } /* Split the string on the separator, then seek past it */ modifier[0] = '\0'; modifier++; if (strcmp(category, "addr") == 0) f_addr = strdup(modifier); else if (strcmp(category, "ether") == 0) f_ether = strdup(modifier); else if (strcmp(category, "inet") == 0) f_inet = strdup(modifier); else if (strcmp(category, "inet6") == 0) f_inet6 = strdup(modifier); } free(formatstr); } #ifdef WITHOUT_NETLINK static struct ifaddrs * sortifaddrs(struct ifaddrs *list, int (*compare)(struct ifaddrs *, struct ifaddrs *, struct ifa_queue *), struct ifa_queue *q) { struct ifaddrs *right, *temp, *last, *result, *next, *tail; right = list; temp = list; last = list; result = NULL; next = NULL; tail = NULL; if (!list || !list->ifa_next) return (list); while (temp && temp->ifa_next) { last = right; right = right->ifa_next; temp = temp->ifa_next->ifa_next; } last->ifa_next = NULL; list = sortifaddrs(list, compare, q); right = sortifaddrs(right, compare, q); while (list || right) { if (!right) { next = list; list = list->ifa_next; } else if (!list) { next = right; right = right->ifa_next; } else if (compare(list, right, q) <= 0) { next = list; list = list->ifa_next; } else { next = right; right = right->ifa_next; } if (!result) result = next; else tail->ifa_next = next; tail = next; } return (result); } #endif static void printifnamemaybe(void) { if (ifname_to_print[0] != '\0') printf("%s\n", ifname_to_print); } static void list_interfaces(if_ctx *ctx) { #ifdef WITHOUT_NETLINK list_interfaces_ioctl(ctx); #else list_interfaces_nl(ctx->args); #endif } static char * args_peek(struct ifconfig_args *args) { if (args->argc > 0) return (args->argv[0]); return (NULL); } static char * args_pop(struct ifconfig_args *args) { if (args->argc == 0) return (NULL); char *arg = args->argv[0]; args->argc--; args->argv++; return (arg); } static void args_parse(struct ifconfig_args *args, int argc, char *argv[]) { char options[1024]; struct option *p; int c; /* Parse leading line options */ strlcpy(options, "G:adf:j:klmnuv", sizeof(options)); for (p = opts; p != NULL; p = p->next) strlcat(options, p->opt, sizeof(options)); while ((c = getopt(argc, argv, options)) != -1) { switch (c) { case 'a': /* scan all interfaces */ args->all = true; break; case 'd': /* restrict scan to "down" interfaces */ args->downonly = true; break; case 'f': if (optarg == NULL) usage(); setformat(optarg); break; case 'G': if (optarg == NULL || args->all == 0) usage(); args->nogroup = optarg; break; case 'j': #ifdef JAIL if (optarg == NULL) usage(); args->jail_name = optarg; #else Perror("not built with jail support"); #endif break; case 'k': args->printkeys = true; break; case 'l': /* scan interface names only */ args->namesonly = true; break; case 'm': /* show media choices in status */ args->supmedia = true; break; case 'n': /* suppress module loading */ args->noload = true; break; case 'u': /* restrict scan to "up" interfaces */ args->uponly = true; break; case 'v': args->verbose++; break; case 'g': if (args->all) { if (optarg == NULL) usage(); args->matchgroup = optarg; break; } /* FALLTHROUGH */ default: for (p = opts; p != NULL; p = p->next) if (p->opt[0] == c) { p->cb(optarg); break; } if (p == NULL) usage(); break; } } argc -= optind; argv += optind; /* -l cannot be used with -a or -m */ if (args->namesonly && (args->all || args->supmedia)) usage(); /* nonsense.. */ if (args->uponly && args->downonly) usage(); /* no arguments is equivalent to '-a' */ if (!args->namesonly && argc < 1) args->all = 1; /* -a and -l allow an address family arg to limit the output */ if (args->all || args->namesonly) { if (argc > 1) usage(); if (argc == 1) { const struct afswtch *afp = af_getbyname(*argv); if (afp == NULL) { warnx("Address family '%s' unknown.", *argv); usage(); } if (afp->af_name != NULL) argc--, argv++; /* leave with afp non-zero */ args->afp = afp; } } else { /* not listing, need an argument */ if (argc < 1) usage(); } args->argc = argc; args->argv = argv; } static int ifconfig(if_ctx *ctx, int iscreate, const struct afswtch *uafp) { #ifdef WITHOUT_NETLINK return (ifconfig_ioctl(ctx, iscreate, uafp)); #else return (ifconfig_nl(ctx, iscreate, uafp)); #endif } static bool isargcreate(const char *arg) { if (arg == NULL) return (false); if (strcmp(arg, "create") == 0 || strcmp(arg, "plumb") == 0) return (true); return (false); } static bool isnametoolong(const char *ifname) { return (strlen(ifname) >= IFNAMSIZ); } int main(int ac, char *av[]) { char *envformat; int flags; #ifdef JAIL int jid; #endif struct ifconfig_args _args = {}; struct ifconfig_args *args = &_args; struct ifconfig_context ctx = { .args = args, .io_s = -1, }; f_inet = f_inet6 = f_ether = f_addr = NULL; lifh = ifconfig_open(); if (lifh == NULL) err(EXIT_FAILURE, "ifconfig_open"); envformat = getenv("IFCONFIG_FORMAT"); if (envformat != NULL) setformat(envformat); /* * Ensure we print interface name when expected to, * even if we terminate early due to error. */ atexit(printifnamemaybe); args_parse(args, ac, av); #ifdef JAIL if (args->jail_name) { jid = jail_getid(args->jail_name); if (jid == -1) Perror("jail not found"); if (jail_attach(jid) != 0) Perror("cannot attach to jail"); } #endif if (!args->all && !args->namesonly) { /* not listing, need an argument */ args->ifname = args_pop(args); ctx.ifname = args->ifname; /* check and maybe load support for this interface */ ifmaybeload(args, args->ifname); char *arg = args_peek(args); if (if_nametoindex(args->ifname) == 0) { /* * NOTE: We must special-case the `create' command * right here as we would otherwise fail when trying * to find the interface. */ if (isargcreate(arg)) { if (isnametoolong(args->ifname)) errx(1, "%s: cloning name too long", args->ifname); ifconfig(&ctx, 1, NULL); exit(exit_code); } #ifdef JAIL /* * NOTE: We have to special-case the `-vnet' command * right here as we would otherwise fail when trying * to find the interface as it lives in another vnet. */ if (arg != NULL && (strcmp(arg, "-vnet") == 0)) { if (isnametoolong(args->ifname)) errx(1, "%s: interface name too long", args->ifname); ifconfig(&ctx, 0, NULL); exit(exit_code); } #endif errx(1, "interface %s does not exist", args->ifname); } else { /* * Do not allow use `create` command as hostname if * address family is not specified. */ if (isargcreate(arg)) { if (args->argc == 1) errx(1, "interface %s already exists", args->ifname); args_pop(args); } } } /* Check for address family */ if (args->argc > 0) { args->afp = af_getbyname(args_peek(args)); if (args->afp != NULL) args_pop(args); } /* * Check for a requested configuration action on a single interface, * which doesn't require building, sorting, and searching the entire * system address list */ if ((args->argc > 0) && (args->ifname != NULL)) { if (isnametoolong(args->ifname)) warnx("%s: interface name too long, skipping", args->ifname); else { flags = getifflags(args->ifname, -1, false); if (!(((flags & IFF_CANTCONFIG) != 0) || (args->downonly && (flags & IFF_UP) != 0) || (args->uponly && (flags & IFF_UP) == 0))) ifconfig(&ctx, 0, args->afp); } goto done; } args->allfamilies = args->afp == NULL; list_interfaces(&ctx); done: freeformat(); ifconfig_close(lifh); exit(exit_code); } bool match_ether(const struct sockaddr_dl *sdl) { switch (sdl->sdl_type) { case IFT_ETHER: case IFT_L2VLAN: case IFT_BRIDGE: if (sdl->sdl_alen == ETHER_ADDR_LEN) return (true); default: return (false); } } bool match_if_flags(struct ifconfig_args *args, int if_flags) { if ((if_flags & IFF_CANTCONFIG) != 0) return (false); if (args->downonly && (if_flags & IFF_UP) != 0) return (false); if (args->uponly && (if_flags & IFF_UP) == 0) return (false); return (true); } #ifdef WITHOUT_NETLINK static bool match_afp(const struct afswtch *afp, int sa_family, const struct sockaddr_dl *sdl) { if (afp == NULL) return (true); /* special case for "ether" address family */ if (!strcmp(afp->af_name, "ether")) { if (sdl == NULL || !match_ether(sdl)) return (false); return (true); } return (afp->af_af == sa_family); } static void list_interfaces_ioctl(if_ctx *ctx) { struct ifa_queue q = TAILQ_HEAD_INITIALIZER(q); struct ifaddrs *ifap, *sifap, *ifa; struct ifa_order_elt *cur, *tmp; char *namecp = NULL; int ifindex; struct ifconfig_args *args = ctx->args; if (getifaddrs(&ifap) != 0) err(EXIT_FAILURE, "getifaddrs"); char *cp = NULL; if (calcorders(ifap, &q) != 0) err(EXIT_FAILURE, "calcorders"); sifap = sortifaddrs(ifap, cmpifaddrs, &q); TAILQ_FOREACH_SAFE(cur, &q, link, tmp) free(cur); ifindex = 0; for (ifa = sifap; ifa; ifa = ifa->ifa_next) { struct ifreq paifr = {}; const struct sockaddr_dl *sdl; strlcpy(paifr.ifr_name, ifa->ifa_name, sizeof(paifr.ifr_name)); if (sizeof(paifr.ifr_addr) >= ifa->ifa_addr->sa_len) { memcpy(&paifr.ifr_addr, ifa->ifa_addr, ifa->ifa_addr->sa_len); } if (args->ifname != NULL && strcmp(args->ifname, ifa->ifa_name) != 0) continue; if (ifa->ifa_addr->sa_family == AF_LINK) sdl = satosdl_c(ifa->ifa_addr); else sdl = NULL; if (cp != NULL && strcmp(cp, ifa->ifa_name) == 0 && !args->namesonly) continue; if (isnametoolong(ifa->ifa_name)) { warnx("%s: interface name too long, skipping", ifa->ifa_name); continue; } cp = ifa->ifa_name; if (!match_if_flags(args, ifa->ifa_flags)) continue; if (!group_member(ifa->ifa_name, args->matchgroup, args->nogroup)) continue; ctx->ifname = cp; /* * Are we just listing the interfaces? */ if (args->namesonly) { if (namecp == cp) continue; if (!match_afp(args->afp, ifa->ifa_addr->sa_family, sdl)) continue; namecp = cp; ifindex++; if (ifindex > 1) printf(" "); fputs(cp, stdout); continue; } ifindex++; if (args->argc > 0) ifconfig(ctx, 0, args->afp); else status(ctx, sdl, ifa); } if (args->namesonly) printf("\n"); freeifaddrs(ifap); } #endif /* * Returns true if an interface should be listed because any its groups * matches shell pattern "match" and none of groups matches pattern "nomatch". * If any pattern is NULL, corresponding condition is skipped. */ bool group_member(const char *ifname, const char *match, const char *nomatch) { static int sock = -1; struct ifgroupreq ifgr; struct ifg_req *ifg; unsigned int len; bool matched, nomatched; /* Sanity checks. */ if (match == NULL && nomatch == NULL) return (true); if (ifname == NULL) return (false); memset(&ifgr, 0, sizeof(ifgr)); strlcpy(ifgr.ifgr_name, ifname, IFNAMSIZ); /* The socket is opened once. Let _exit() close it. */ if (sock == -1) { sock = socket(AF_LOCAL, SOCK_DGRAM, 0); if (sock == -1) errx(1, "%s: socket(AF_LOCAL,SOCK_DGRAM)", __func__); } /* Determine amount of memory for the list of groups. */ if (ioctl(sock, SIOCGIFGROUP, (caddr_t)&ifgr) == -1) { if (errno == EINVAL || errno == ENOTTY) return (false); else errx(1, "%s: SIOCGIFGROUP", __func__); } /* Obtain the list of groups. */ len = ifgr.ifgr_len; ifgr.ifgr_groups = (struct ifg_req *)calloc(len / sizeof(*ifg), sizeof(*ifg)); if (ifgr.ifgr_groups == NULL) errx(1, "%s: no memory", __func__); if (ioctl(sock, SIOCGIFGROUP, (caddr_t)&ifgr) == -1) errx(1, "%s: SIOCGIFGROUP", __func__); /* Perform matching. */ matched = false; nomatched = true; for (ifg = ifgr.ifgr_groups; ifg && len >= sizeof(*ifg); ifg++) { len -= sizeof(*ifg); if (match && !matched) matched = !fnmatch(match, ifg->ifgrq_group, 0); if (nomatch && nomatched) nomatched = fnmatch(nomatch, ifg->ifgrq_group, 0); } free(ifgr.ifgr_groups); if (match && !nomatch) return (matched); if (!match && nomatch) return (nomatched); return (matched && nomatched); } static struct afswtch *afs = NULL; void af_register(struct afswtch *p) { p->af_next = afs; afs = p; } static struct afswtch * af_getbyname(const char *name) { struct afswtch *afp; for (afp = afs; afp != NULL; afp = afp->af_next) if (strcmp(afp->af_name, name) == 0) return afp; return NULL; } struct afswtch * af_getbyfamily(int af) { struct afswtch *afp; for (afp = afs; afp != NULL; afp = afp->af_next) if (afp->af_af == af) return afp; return NULL; } void af_other_status(if_ctx *ctx) { struct afswtch *afp; uint8_t afmask[howmany(AF_MAX, NBBY)]; memset(afmask, 0, sizeof(afmask)); for (afp = afs; afp != NULL; afp = afp->af_next) { if (afp->af_other_status == NULL) continue; if (afp->af_af != AF_UNSPEC && isset(afmask, afp->af_af)) continue; afp->af_other_status(ctx); setbit(afmask, afp->af_af); } } static void af_all_tunnel_status(if_ctx *ctx) { struct afswtch *afp; uint8_t afmask[howmany(AF_MAX, NBBY)]; memset(afmask, 0, sizeof(afmask)); for (afp = afs; afp != NULL; afp = afp->af_next) { if (afp->af_status_tunnel == NULL) continue; if (afp->af_af != AF_UNSPEC && isset(afmask, afp->af_af)) continue; afp->af_status_tunnel(ctx); setbit(afmask, afp->af_af); } } static struct cmd *cmds = NULL; void cmd_register(struct cmd *p) { p->c_next = cmds; cmds = p; } static const struct cmd * cmd_lookup(const char *name, int iscreate) { const struct cmd *p; for (p = cmds; p != NULL; p = p->c_next) if (strcmp(name, p->c_name) == 0) { if (iscreate) { if (p->c_iscloneop) return p; } else { if (!p->c_iscloneop) return p; } } return NULL; } struct callback { callback_func *cb_func; void *cb_arg; struct callback *cb_next; }; static struct callback *callbacks = NULL; void callback_register(callback_func *func, void *arg) { struct callback *cb; cb = malloc(sizeof(struct callback)); if (cb == NULL) errx(1, "unable to allocate memory for callback"); cb->cb_func = func; cb->cb_arg = arg; cb->cb_next = callbacks; callbacks = cb; } /* specially-handled commands */ static void setifaddr(if_ctx *ctx, const char *addr, int param); static const struct cmd setifaddr_cmd = DEF_CMD("ifaddr", 0, setifaddr); static void setifdstaddr(if_ctx *ctx, const char *addr, int param __unused); static const struct cmd setifdstaddr_cmd = DEF_CMD("ifdstaddr", 0, setifdstaddr); int af_exec_ioctl(if_ctx *ctx, unsigned long action, void *data) { struct ifreq *req = (struct ifreq *)data; strlcpy(req->ifr_name, ctx->ifname, sizeof(req->ifr_name)); if (ioctl_ctx(ctx, action, req) == 0) return (0); return (errno); } static void delifaddr(if_ctx *ctx, const struct afswtch *afp) { int error; if (afp->af_exec == NULL) { warnx("interface %s cannot change %s addresses!", ctx->ifname, afp->af_name); clearaddr = 0; return; } error = afp->af_exec(ctx, afp->af_difaddr, afp->af_ridreq); if (error != 0) { if (error == EADDRNOTAVAIL && (doalias >= 0)) { /* means no previous address for interface */ } else Perrorc("ioctl (SIOCDIFADDR)", error); } } static void addifaddr(if_ctx *ctx, const struct afswtch *afp) { if (afp->af_exec == NULL) { warnx("interface %s cannot change %s addresses!", ctx->ifname, afp->af_name); newaddr = 0; return; } if (setaddr || setmask) { int error = afp->af_exec(ctx, afp->af_aifaddr, afp->af_addreq); if (error != 0) Perrorc("ioctl (SIOCAIFADDR)", error); } } int ifconfig_ioctl(if_ctx *orig_ctx, int iscreate, const struct afswtch *uafp) { const struct afswtch *afp, *nafp; const struct cmd *p; struct callback *cb; int s; int argc = orig_ctx->args->argc; char *const *argv = orig_ctx->args->argv; struct ifconfig_context _ctx = { .args = orig_ctx->args, .io_ss = orig_ctx->io_ss, .ifname = orig_ctx->ifname, }; struct ifconfig_context *ctx = &_ctx; struct ifreq ifr = {}; strlcpy(ifr.ifr_name, ctx->ifname, sizeof ifr.ifr_name); afp = NULL; if (uafp != NULL) afp = uafp; /* * This is the historical "accident" allowing users to configure IPv4 * addresses without the "inet" keyword which while a nice feature has * proven to complicate other things. We cannot remove this but only * make sure we will never have a similar implicit default for IPv6 or * any other address familiy. We need a fallback though for * ifconfig IF up/down etc. to work without INET support as people * never used ifconfig IF link up/down, etc. either. */ #ifndef RESCUE #ifdef INET if (afp == NULL && feature_present("inet")) afp = af_getbyname("inet"); #endif #endif if (afp == NULL) afp = af_getbyname("link"); if (afp == NULL) { warnx("Please specify an address_family."); usage(); } top: ifr.ifr_addr.sa_family = afp->af_af == AF_LINK || afp->af_af == AF_UNSPEC ? AF_LOCAL : afp->af_af; if ((s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0)) < 0 && (uafp != NULL || errno != EAFNOSUPPORT || (s = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0)) err(1, "socket(family %u,SOCK_DGRAM)", ifr.ifr_addr.sa_family); ctx->io_s = s; ctx->afp = afp; while (argc > 0) { p = cmd_lookup(*argv, iscreate); if (iscreate && p == NULL) { /* * Push the clone create callback so the new * device is created and can be used for any * remaining arguments. */ cb = callbacks; if (cb == NULL) errx(1, "internal error, no callback"); callbacks = cb->cb_next; cb->cb_func(ctx, cb->cb_arg); iscreate = 0; /* * Handle any address family spec that * immediately follows and potentially * recreate the socket. */ nafp = af_getbyname(*argv); if (nafp != NULL) { argc--, argv++; if (nafp != afp) { close(s); afp = nafp; goto top; } } /* * Look for a normal parameter. */ continue; } if (p == NULL) { /* * Not a recognized command, choose between setting * the interface address and the dst address. */ p = (setaddr ? &setifdstaddr_cmd : &setifaddr_cmd); } if (p->c_parameter == NEXTARG && p->c_u.c_func) { if (argv[1] == NULL) errx(1, "'%s' requires argument", p->c_name); p->c_u.c_func(ctx, argv[1], 0); argc--, argv++; } else if (p->c_parameter == OPTARG && p->c_u.c_func) { p->c_u.c_func(ctx, argv[1], 0); if (argv[1] != NULL) argc--, argv++; } else if (p->c_parameter == NEXTARG2 && p->c_u.c_func2) { if (argc < 3) errx(1, "'%s' requires 2 arguments", p->c_name); p->c_u.c_func2(ctx, argv[1], argv[2]); argc -= 2, argv += 2; } else if (p->c_parameter == SPARAM && p->c_u.c_func3) { p->c_u.c_func3(ctx, *argv, p->c_sparameter); } else if (p->c_u.c_func) p->c_u.c_func(ctx, *argv, p->c_parameter); argc--, argv++; } /* * Do any post argument processing required by the address family. */ if (afp->af_postproc != NULL) afp->af_postproc(ctx, newaddr, getifflags(ctx->ifname, s, true)); /* * Do deferred callbacks registered while processing * command-line arguments. */ for (cb = callbacks; cb != NULL; cb = cb->cb_next) cb->cb_func(ctx, cb->cb_arg); /* * Do deferred operations. */ if (clearaddr) delifaddr(ctx, afp); if (newaddr) addifaddr(ctx, afp); close(s); return(0); } static void setifaddr(if_ctx *ctx, const char *addr, int param __unused) { const struct afswtch *afp = ctx->afp; if (afp->af_getaddr == NULL) return; /* * Delay the ioctl to set the interface addr until flags are all set. * The address interpretation may depend on the flags, * and the flags may change when the address is set. */ setaddr++; if (doalias == 0 && afp->af_af != AF_LINK) clearaddr = 1; afp->af_getaddr(addr, (doalias >= 0 ? ADDR : RIDADDR)); } static void settunnel(if_ctx *ctx, const char *src, const char *dst) { const struct afswtch *afp = ctx->afp; struct addrinfo *srcres, *dstres; int ecode; if (afp->af_settunnel == NULL) { warn("address family %s does not support tunnel setup", afp->af_name); return; } if ((ecode = getaddrinfo(src, NULL, NULL, &srcres)) != 0) errx(1, "error in parsing address string: %s", gai_strerror(ecode)); if ((ecode = getaddrinfo(dst, NULL, NULL, &dstres)) != 0) errx(1, "error in parsing address string: %s", gai_strerror(ecode)); if (srcres->ai_addr->sa_family != dstres->ai_addr->sa_family) errx(1, "source and destination address families do not match"); afp->af_settunnel(ctx, srcres, dstres); freeaddrinfo(srcres); freeaddrinfo(dstres); } static void deletetunnel(if_ctx *ctx, const char *vname __unused, int param __unused) { struct ifreq ifr = {}; if (ioctl_ctx_ifr(ctx, SIOCDIFPHYADDR, &ifr) < 0) err(1, "SIOCDIFPHYADDR"); } #ifdef JAIL static void setifvnet(if_ctx *ctx, const char *jname, int dummy __unused) { struct ifreq ifr = {}; ifr.ifr_jid = jail_getid(jname); if (ifr.ifr_jid < 0) errx(1, "%s", jail_errmsg); if (ioctl_ctx_ifr(ctx, SIOCSIFVNET, &ifr) < 0) err(1, "SIOCSIFVNET"); } static void setifrvnet(if_ctx *ctx, const char *jname, int dummy __unused) { struct ifreq ifr = {}; ifr.ifr_jid = jail_getid(jname); if (ifr.ifr_jid < 0) errx(1, "%s", jail_errmsg); if (ioctl_ctx_ifr(ctx, SIOCSIFRVNET, &ifr) < 0) err(1, "SIOCSIFRVNET(%d, %s)", ifr.ifr_jid, ifr.ifr_name); } #endif static void setifnetmask(if_ctx *ctx, const char *addr, int dummy __unused) { const struct afswtch *afp = ctx->afp; if (afp->af_getaddr != NULL) { setmask++; afp->af_getaddr(addr, MASK); } } static void setifbroadaddr(if_ctx *ctx, const char *addr, int dummy __unused) { const struct afswtch *afp = ctx->afp; if (afp->af_getaddr != NULL) afp->af_getaddr(addr, BRDADDR); } static void notealias(if_ctx *ctx, const char *addr __unused, int param) { const struct afswtch *afp = ctx->afp; if (setaddr && doalias == 0 && param < 0) { if (afp->af_copyaddr != NULL) afp->af_copyaddr(ctx, RIDADDR, ADDR); } doalias = param; if (param < 0) { clearaddr = 1; newaddr = 0; } else clearaddr = 0; } static void setifdstaddr(if_ctx *ctx, const char *addr, int param __unused) { const struct afswtch *afp = ctx->afp; if (afp->af_getaddr != NULL) afp->af_getaddr(addr, DSTADDR); } static int getifflags(const char *ifname, int us, bool err_ok) { struct ifreq my_ifr; int s; memset(&my_ifr, 0, sizeof(my_ifr)); (void) strlcpy(my_ifr.ifr_name, ifname, sizeof(my_ifr.ifr_name)); if (us < 0) { if ((s = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0) err(1, "socket(family AF_LOCAL,SOCK_DGRAM"); } else s = us; if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&my_ifr) < 0) { if (!err_ok) { Perror("ioctl (SIOCGIFFLAGS)"); exit(1); } } if (us < 0) close(s); return ((my_ifr.ifr_flags & 0xffff) | (my_ifr.ifr_flagshigh << 16)); } /* * Note: doing an SIOCIGIFFLAGS scribbles on the union portion * of the ifreq structure, which may confuse other parts of ifconfig. * Make a private copy so we can avoid that. */ static void clearifflags(if_ctx *ctx, const char *vname, int value) { struct ifreq my_ifr; int flags; flags = getifflags(ctx->ifname, ctx->io_s, false); flags &= ~value; memset(&my_ifr, 0, sizeof(my_ifr)); strlcpy(my_ifr.ifr_name, ctx->ifname, sizeof(my_ifr.ifr_name)); my_ifr.ifr_flags = flags & 0xffff; my_ifr.ifr_flagshigh = flags >> 16; if (ioctl(ctx->io_s, SIOCSIFFLAGS, (caddr_t)&my_ifr) < 0) Perror(vname); } static void setifflags(if_ctx *ctx, const char *vname, int value) { struct ifreq my_ifr; int flags; flags = getifflags(ctx->ifname, ctx->io_s, false); flags |= value; memset(&my_ifr, 0, sizeof(my_ifr)); strlcpy(my_ifr.ifr_name, ctx->ifname, sizeof(my_ifr.ifr_name)); my_ifr.ifr_flags = flags & 0xffff; my_ifr.ifr_flagshigh = flags >> 16; if (ioctl(ctx->io_s, SIOCSIFFLAGS, (caddr_t)&my_ifr) < 0) Perror(vname); } void clearifcap(if_ctx *ctx, const char *vname, int value) { struct ifreq ifr = {}; int flags; if (ioctl_ctx_ifr(ctx, SIOCGIFCAP, &ifr) < 0) { Perror("ioctl (SIOCGIFCAP)"); exit(1); } flags = ifr.ifr_curcap; flags &= ~value; flags &= ifr.ifr_reqcap; /* Check for no change in capabilities. */ if (ifr.ifr_curcap == flags) return; ifr.ifr_reqcap = flags; if (ioctl_ctx(ctx, SIOCSIFCAP, &ifr) < 0) Perror(vname); } void setifcap(if_ctx *ctx, const char *vname, int value) { struct ifreq ifr = {}; int flags; if (ioctl_ctx_ifr(ctx, SIOCGIFCAP, &ifr) < 0) { Perror("ioctl (SIOCGIFCAP)"); exit(1); } flags = ifr.ifr_curcap; flags |= value; flags &= ifr.ifr_reqcap; /* Check for no change in capabilities. */ if (ifr.ifr_curcap == flags) return; ifr.ifr_reqcap = flags; if (ioctl_ctx(ctx, SIOCSIFCAP, &ifr) < 0) Perror(vname); } void setifcapnv(if_ctx *ctx, const char *vname, const char *arg) { nvlist_t *nvcap; void *buf; char *marg, *mopt; size_t nvbuflen; bool neg; struct ifreq ifr = {}; if (ioctl_ctx_ifr(ctx, SIOCGIFCAP, &ifr) < 0) Perror("ioctl (SIOCGIFCAP)"); if ((ifr.ifr_curcap & IFCAP_NV) == 0) { warnx("IFCAP_NV not supported"); return; /* Not exit() */ } marg = strdup(arg); if (marg == NULL) Perror("strdup"); nvcap = nvlist_create(0); if (nvcap == NULL) Perror("nvlist_create"); while ((mopt = strsep(&marg, ",")) != NULL) { neg = *mopt == '-'; if (neg) mopt++; if (strcmp(mopt, "rxtls") == 0) { nvlist_add_bool(nvcap, "rxtls4", !neg); nvlist_add_bool(nvcap, "rxtls6", !neg); } else { nvlist_add_bool(nvcap, mopt, !neg); } } buf = nvlist_pack(nvcap, &nvbuflen); if (buf == NULL) { errx(1, "nvlist_pack error"); exit(1); } ifr.ifr_cap_nv.buf_length = ifr.ifr_cap_nv.length = nvbuflen; ifr.ifr_cap_nv.buffer = buf; if (ioctl_ctx(ctx, SIOCSIFCAPNV, (caddr_t)&ifr) < 0) Perror(vname); free(buf); nvlist_destroy(nvcap); free(marg); } static void setifmetric(if_ctx *ctx, const char *val, int dummy __unused) { struct ifreq ifr = {}; ifr.ifr_metric = atoi(val); if (ioctl_ctx_ifr(ctx, SIOCSIFMETRIC, &ifr) < 0) err(1, "ioctl SIOCSIFMETRIC (set metric)"); } static void setifmtu(if_ctx *ctx, const char *val, int dummy __unused) { struct ifreq ifr = {}; ifr.ifr_mtu = atoi(val); if (ioctl_ctx_ifr(ctx, SIOCSIFMTU, &ifr) < 0) err(1, "ioctl SIOCSIFMTU (set mtu)"); } static void setifpcp(if_ctx *ctx, const char *val, int arg __unused) { struct ifreq ifr = {}; u_long ul; char *endp; ul = strtoul(val, &endp, 0); if (*endp != '\0') errx(1, "invalid value for pcp"); if (ul > 7) errx(1, "value for pcp out of range"); ifr.ifr_lan_pcp = ul; if (ioctl_ctx_ifr(ctx, SIOCSLANPCP, &ifr) == -1) err(1, "SIOCSLANPCP"); } static void disableifpcp(if_ctx *ctx, const char *val __unused, int arg __unused) { struct ifreq ifr = {}; ifr.ifr_lan_pcp = IFNET_PCP_NONE; if (ioctl_ctx_ifr(ctx, SIOCSLANPCP, &ifr) == -1) err(1, "SIOCSLANPCP"); } static void setifname(if_ctx *ctx, const char *val, int dummy __unused) { struct ifreq ifr = {}; char *newname; ifr_set_name(&ifr, ctx->ifname); newname = strdup(val); if (newname == NULL) err(1, "no memory to set ifname"); ifr.ifr_data = newname; if (ioctl_ctx(ctx, SIOCSIFNAME, (caddr_t)&ifr) < 0) { free(newname); err(1, "ioctl SIOCSIFNAME (set name)"); } ifname_update(ctx, newname); free(newname); } static void setifdescr(if_ctx *ctx, const char *val, int dummy __unused) { struct ifreq ifr = {}; char *newdescr; ifr.ifr_buffer.length = strlen(val) + 1; if (ifr.ifr_buffer.length == 1) { ifr.ifr_buffer.buffer = newdescr = NULL; ifr.ifr_buffer.length = 0; } else { newdescr = strdup(val); ifr.ifr_buffer.buffer = newdescr; if (newdescr == NULL) { warn("no memory to set ifdescr"); return; } } if (ioctl_ctx_ifr(ctx, SIOCSIFDESCR, &ifr) < 0) err(1, "ioctl SIOCSIFDESCR (set descr)"); free(newdescr); } static void unsetifdescr(if_ctx *ctx, const char *val __unused, int value __unused) { setifdescr(ctx, "", 0); } #ifdef WITHOUT_NETLINK #define IFFBITS \ "\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\7RUNNING" \ "\10NOARP\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX\15LINK0\16LINK1\17LINK2" \ "\20MULTICAST\22PPROMISC\23MONITOR\24STATICARP\25STICKYARP" #define IFCAPBITS \ "\020\1RXCSUM\2TXCSUM\3NETCONS\4VLAN_MTU\5VLAN_HWTAGGING\6JUMBO_MTU\7POLLING" \ "\10VLAN_HWCSUM\11TSO4\12TSO6\13LRO\14WOL_UCAST\15WOL_MCAST\16WOL_MAGIC" \ "\17TOE4\20TOE6\21VLAN_HWFILTER\23VLAN_HWTSO\24LINKSTATE\25NETMAP" \ "\26RXCSUM_IPV6\27TXCSUM_IPV6\31TXRTLMT\32HWRXTSTMP\33NOMAP\34TXTLS4\35TXTLS6" \ "\36VXLAN_HWCSUM\37VXLAN_HWTSO\40TXTLS_RTLMT" static void print_ifcap_nv(if_ctx *ctx) { struct ifreq ifr = {}; nvlist_t *nvcap; const char *nvname; void *buf, *cookie; bool first, val; int type; buf = malloc(IFR_CAP_NV_MAXBUFSIZE); if (buf == NULL) Perror("malloc"); ifr.ifr_cap_nv.buffer = buf; ifr.ifr_cap_nv.buf_length = IFR_CAP_NV_MAXBUFSIZE; if (ioctl_ctx_ifr(ctx, SIOCGIFCAPNV, &ifr) != 0) Perror("ioctl (SIOCGIFCAPNV)"); nvcap = nvlist_unpack(ifr.ifr_cap_nv.buffer, ifr.ifr_cap_nv.length, 0); if (nvcap == NULL) Perror("nvlist_unpack"); printf("\toptions"); cookie = NULL; for (first = true;; first = false) { nvname = nvlist_next(nvcap, &type, &cookie); if (nvname == NULL) { printf("\n"); break; } if (type == NV_TYPE_BOOL) { val = nvlist_get_bool(nvcap, nvname); if (val) { printf("%c%s", first ? ' ' : ',', nvname); } } } if (ctx->args->supmedia) { printf("\tcapabilities"); cookie = NULL; for (first = true;; first = false) { nvname = nvlist_next(nvcap, &type, &cookie); if (nvname == NULL) { printf("\n"); break; } if (type == NV_TYPE_BOOL) printf("%c%s", first ? ' ' : ',', nvname); } } nvlist_destroy(nvcap); free(buf); if (ioctl_ctx(ctx, SIOCGIFCAP, (caddr_t)&ifr) != 0) Perror("ioctl (SIOCGIFCAP)"); } static void print_ifcap(if_ctx *ctx) { struct ifreq ifr = {}; if (ioctl_ctx_ifr(ctx, SIOCGIFCAP, &ifr) != 0) return; if ((ifr.ifr_curcap & IFCAP_NV) != 0) print_ifcap_nv(ctx); else { printb("\toptions", ifr.ifr_curcap, IFCAPBITS); putchar('\n'); if (ctx->args->supmedia && ifr.ifr_reqcap != 0) { printb("\tcapabilities", ifr.ifr_reqcap, IFCAPBITS); putchar('\n'); } } } #endif void print_ifstatus(if_ctx *ctx) { struct ifstat ifs; strlcpy(ifs.ifs_name, ctx->ifname, sizeof ifs.ifs_name); if (ioctl_ctx(ctx, SIOCGIFSTATUS, &ifs) == 0) printf("%s", ifs.ascii); } void print_metric(if_ctx *ctx) { struct ifreq ifr = {}; if (ioctl_ctx_ifr(ctx, SIOCGIFMETRIC, &ifr) != -1) printf(" metric %d", ifr.ifr_metric); } #ifdef WITHOUT_NETLINK static void print_mtu(if_ctx *ctx) { struct ifreq ifr = {}; if (ioctl_ctx_ifr(ctx, SIOCGIFMTU, &ifr) != -1) printf(" mtu %d", ifr.ifr_mtu); } static void print_description(if_ctx *ctx) { struct ifreq ifr = {}; ifr_set_name(&ifr, ctx->ifname); for (;;) { if ((descr = reallocf(descr, descrlen)) != NULL) { ifr.ifr_buffer.buffer = descr; ifr.ifr_buffer.length = descrlen; if (ioctl_ctx(ctx, SIOCGIFDESCR, &ifr) == 0) { if (ifr.ifr_buffer.buffer == descr) { if (strlen(descr) > 0) printf("\tdescription: %s\n", descr); } else if (ifr.ifr_buffer.length > descrlen) { descrlen = ifr.ifr_buffer.length; continue; } } } else warn("unable to allocate memory for interface" "description"); break; } } /* * Print the status of the interface. If an address family was * specified, show only it; otherwise, show them all. */ static void status(if_ctx *ctx, const struct sockaddr_dl *sdl __unused, struct ifaddrs *ifa) { struct ifaddrs *ift; int s, old_s; struct ifconfig_args *args = ctx->args; bool allfamilies = args->afp == NULL; struct ifreq ifr = {}; if (args->afp == NULL) ifr.ifr_addr.sa_family = AF_LOCAL; else ifr.ifr_addr.sa_family = args->afp->af_af == AF_LINK ? AF_LOCAL : args->afp->af_af; s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0); if (s < 0) err(1, "socket(family %u,SOCK_DGRAM)", ifr.ifr_addr.sa_family); old_s = ctx->io_s; ctx->io_s = s; printf("%s: ", ctx->ifname); printb("flags", ifa->ifa_flags, IFFBITS); print_metric(ctx); print_mtu(ctx); putchar('\n'); print_description(ctx); print_ifcap(ctx); tunnel_status(ctx); for (ift = ifa; ift != NULL; ift = ift->ifa_next) { if (ift->ifa_addr == NULL) continue; if (strcmp(ifa->ifa_name, ift->ifa_name) != 0) continue; if (allfamilies) { const struct afswtch *p; p = af_getbyfamily(ift->ifa_addr->sa_family); if (p != NULL && p->af_status != NULL) p->af_status(ctx, ift); } else if (args->afp->af_af == ift->ifa_addr->sa_family) args->afp->af_status(ctx, ift); } #if 0 if (allfamilies || afp->af_af == AF_LINK) { const struct afswtch *lafp; /* * Hack; the link level address is received separately * from the routing information so any address is not * handled above. Cobble together an entry and invoke * the status method specially. */ lafp = af_getbyname("lladdr"); if (lafp != NULL) { info.rti_info[RTAX_IFA] = (struct sockaddr *)sdl; lafp->af_status(s, &info); } } #endif if (allfamilies) af_other_status(ctx); else if (args->afp->af_other_status != NULL) args->afp->af_other_status(ctx); print_ifstatus(ctx); if (args->verbose > 0) sfp_status(ctx); close(s); ctx->io_s = old_s; return; } #endif void tunnel_status(if_ctx *ctx) { af_all_tunnel_status(ctx); } static void Perrorc(const char *cmd, int error) { switch (errno) { case ENXIO: errx(1, "%s: no such interface", cmd); break; case EPERM: errx(1, "%s: permission denied", cmd); break; default: errc(1, error, "%s", cmd); } } void Perror(const char *cmd) { Perrorc(cmd, errno); } /* * Print a value a la the %b format of the kernel's printf */ void printb(const char *s, unsigned v, const char *bits) { int i, any = 0; char c; if (bits && *bits == 8) printf("%s=%o", s, v); else printf("%s=%x", s, v); if (bits) { bits++; putchar('<'); while ((i = *bits++) != '\0') { if (v & (1u << (i-1))) { if (any) putchar(','); any = 1; for (; (c = *bits) > 32; bits++) putchar(c); } else for (; *bits > 32; bits++) ; } putchar('>'); } } void print_vhid(const struct ifaddrs *ifa) { struct if_data *ifd; if (ifa->ifa_data == NULL) return; ifd = ifa->ifa_data; if (ifd->ifi_vhid == 0) return; printf(" vhid %d", ifd->ifi_vhid); } void ifmaybeload(struct ifconfig_args *args, const char *name) { #define MOD_PREFIX_LEN 3 /* "if_" */ struct module_stat mstat; int fileid, modid; char ifkind[IFNAMSIZ + MOD_PREFIX_LEN], ifname[IFNAMSIZ], *dp; const char *cp; struct module_map_entry *mme; bool found; /* loading suppressed by the user */ if (args->noload) return; /* trim the interface number off the end */ strlcpy(ifname, name, sizeof(ifname)); dp = ifname + strlen(ifname) - 1; for (; dp > ifname; dp--) { if (isdigit(*dp)) *dp = '\0'; else break; } /* Either derive it from the map or guess otherwise */ *ifkind = '\0'; found = false; for (unsigned i = 0; i < nitems(module_map); ++i) { mme = &module_map[i]; if (strcmp(mme->ifname, ifname) == 0) { strlcpy(ifkind, mme->kldname, sizeof(ifkind)); found = true; break; } } /* We didn't have an alias for it... we'll guess. */ if (!found) { /* turn interface and unit into module name */ strlcpy(ifkind, "if_", sizeof(ifkind)); strlcat(ifkind, ifname, sizeof(ifkind)); } /* scan files in kernel */ mstat.version = sizeof(struct module_stat); for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) { /* scan modules in file */ for (modid = kldfirstmod(fileid); modid > 0; modid = modfnext(modid)) { if (modstat(modid, &mstat) < 0) continue; /* strip bus name if present */ if ((cp = strchr(mstat.name, '/')) != NULL) { cp++; } else { cp = mstat.name; } /* * Is it already loaded? Don't compare with ifname if * we were specifically told which kld to use. Doing * so could lead to conflicts not trivially solved. */ if ((!found && strcmp(ifname, cp) == 0) || strcmp(ifkind, cp) == 0) return; } } /* * Try to load the module. But ignore failures, because ifconfig can't * infer the names of all drivers (eg mlx4en(4)). */ (void) kldload(ifkind); } static struct cmd basic_cmds[] = { DEF_CMD("up", IFF_UP, setifflags), DEF_CMD("down", IFF_UP, clearifflags), DEF_CMD("arp", IFF_NOARP, clearifflags), DEF_CMD("-arp", IFF_NOARP, setifflags), DEF_CMD("debug", IFF_DEBUG, setifflags), DEF_CMD("-debug", IFF_DEBUG, clearifflags), DEF_CMD_ARG("description", setifdescr), DEF_CMD_ARG("descr", setifdescr), DEF_CMD("-description", 0, unsetifdescr), DEF_CMD("-descr", 0, unsetifdescr), DEF_CMD("promisc", IFF_PPROMISC, setifflags), DEF_CMD("-promisc", IFF_PPROMISC, clearifflags), DEF_CMD("add", IFF_UP, notealias), DEF_CMD("alias", IFF_UP, notealias), DEF_CMD("-alias", -IFF_UP, notealias), DEF_CMD("delete", -IFF_UP, notealias), DEF_CMD("remove", -IFF_UP, notealias), #ifdef notdef #define EN_SWABIPS 0x1000 DEF_CMD("swabips", EN_SWABIPS, setifflags), DEF_CMD("-swabips", EN_SWABIPS, clearifflags), #endif DEF_CMD_ARG("netmask", setifnetmask), DEF_CMD_ARG("metric", setifmetric), DEF_CMD_ARG("broadcast", setifbroadaddr), DEF_CMD_ARG2("tunnel", settunnel), DEF_CMD("-tunnel", 0, deletetunnel), DEF_CMD("deletetunnel", 0, deletetunnel), #ifdef JAIL DEF_CMD_ARG("vnet", setifvnet), DEF_CMD_ARG("-vnet", setifrvnet), #endif DEF_CMD("link0", IFF_LINK0, setifflags), DEF_CMD("-link0", IFF_LINK0, clearifflags), DEF_CMD("link1", IFF_LINK1, setifflags), DEF_CMD("-link1", IFF_LINK1, clearifflags), DEF_CMD("link2", IFF_LINK2, setifflags), DEF_CMD("-link2", IFF_LINK2, clearifflags), DEF_CMD("monitor", IFF_MONITOR, setifflags), DEF_CMD("-monitor", IFF_MONITOR, clearifflags), DEF_CMD("mextpg", IFCAP_MEXTPG, setifcap), DEF_CMD("-mextpg", IFCAP_MEXTPG, clearifcap), DEF_CMD("staticarp", IFF_STATICARP, setifflags), DEF_CMD("-staticarp", IFF_STATICARP, clearifflags), DEF_CMD("stickyarp", IFF_STICKYARP, setifflags), DEF_CMD("-stickyarp", IFF_STICKYARP, clearifflags), DEF_CMD("rxcsum6", IFCAP_RXCSUM_IPV6, setifcap), DEF_CMD("-rxcsum6", IFCAP_RXCSUM_IPV6, clearifcap), DEF_CMD("txcsum6", IFCAP_TXCSUM_IPV6, setifcap), DEF_CMD("-txcsum6", IFCAP_TXCSUM_IPV6, clearifcap), DEF_CMD("rxcsum", IFCAP_RXCSUM, setifcap), DEF_CMD("-rxcsum", IFCAP_RXCSUM, clearifcap), DEF_CMD("txcsum", IFCAP_TXCSUM, setifcap), DEF_CMD("-txcsum", IFCAP_TXCSUM, clearifcap), DEF_CMD("netcons", IFCAP_NETCONS, setifcap), DEF_CMD("-netcons", IFCAP_NETCONS, clearifcap), DEF_CMD_ARG("pcp", setifpcp), DEF_CMD("-pcp", 0, disableifpcp), DEF_CMD("polling", IFCAP_POLLING, setifcap), DEF_CMD("-polling", IFCAP_POLLING, clearifcap), DEF_CMD("tso6", IFCAP_TSO6, setifcap), DEF_CMD("-tso6", IFCAP_TSO6, clearifcap), DEF_CMD("tso4", IFCAP_TSO4, setifcap), DEF_CMD("-tso4", IFCAP_TSO4, clearifcap), DEF_CMD("tso", IFCAP_TSO, setifcap), DEF_CMD("-tso", IFCAP_TSO, clearifcap), DEF_CMD("toe", IFCAP_TOE, setifcap), DEF_CMD("-toe", IFCAP_TOE, clearifcap), DEF_CMD("lro", IFCAP_LRO, setifcap), DEF_CMD("-lro", IFCAP_LRO, clearifcap), DEF_CMD("txtls", IFCAP_TXTLS, setifcap), DEF_CMD("-txtls", IFCAP_TXTLS, clearifcap), DEF_CMD_SARG("rxtls", IFCAP2_RXTLS4_NAME "," IFCAP2_RXTLS6_NAME, setifcapnv), DEF_CMD_SARG("-rxtls", "-"IFCAP2_RXTLS4_NAME ",-" IFCAP2_RXTLS6_NAME, setifcapnv), DEF_CMD("wol", IFCAP_WOL, setifcap), DEF_CMD("-wol", IFCAP_WOL, clearifcap), DEF_CMD("wol_ucast", IFCAP_WOL_UCAST, setifcap), DEF_CMD("-wol_ucast", IFCAP_WOL_UCAST, clearifcap), DEF_CMD("wol_mcast", IFCAP_WOL_MCAST, setifcap), DEF_CMD("-wol_mcast", IFCAP_WOL_MCAST, clearifcap), DEF_CMD("wol_magic", IFCAP_WOL_MAGIC, setifcap), DEF_CMD("-wol_magic", IFCAP_WOL_MAGIC, clearifcap), DEF_CMD("txrtlmt", IFCAP_TXRTLMT, setifcap), DEF_CMD("-txrtlmt", IFCAP_TXRTLMT, clearifcap), DEF_CMD("txtlsrtlmt", IFCAP_TXTLS_RTLMT, setifcap), DEF_CMD("-txtlsrtlmt", IFCAP_TXTLS_RTLMT, clearifcap), DEF_CMD("hwrxtstmp", IFCAP_HWRXTSTMP, setifcap), DEF_CMD("-hwrxtstmp", IFCAP_HWRXTSTMP, clearifcap), DEF_CMD("normal", IFF_LINK0, clearifflags), DEF_CMD("compress", IFF_LINK0, setifflags), DEF_CMD("noicmp", IFF_LINK1, setifflags), DEF_CMD_ARG("mtu", setifmtu), DEF_CMD_ARG("name", setifname), }; static __constructor void ifconfig_ctor(void) { size_t i; for (i = 0; i < nitems(basic_cmds); i++) cmd_register(&basic_cmds[i]); } diff --git a/sbin/ifconfig/ifgif.c b/sbin/ifconfig/ifgif.c index 3a41ef63d1d3..6a4bb8b5a240 100644 --- a/sbin/ifconfig/ifgif.c +++ b/sbin/ifconfig/ifgif.c @@ -1,113 +1,108 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009 Hiroki Sato. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" #define GIFBITS "\020\2IGNORE_SOURCE" static void gif_status(if_ctx *ctx) { int opts; struct ifreq ifr = { .ifr_data = (caddr_t)&opts }; if (ioctl_ctx_ifr(ctx, GIFGOPTS, &ifr) == -1) return; if (opts == 0) return; printb("\toptions", opts, GIFBITS); putchar('\n'); } static void setgifopts(if_ctx *ctx, const char *val __unused, int d) { int opts; struct ifreq ifr = { .ifr_data = (caddr_t)&opts }; if (ioctl_ctx_ifr(ctx, GIFGOPTS, &ifr) == -1) { warn("ioctl(GIFGOPTS)"); return; } if (d < 0) opts &= ~(-d); else opts |= d; if (ioctl_ctx(ctx, GIFSOPTS, &ifr) == -1) { warn("ioctl(GIFSOPTS)"); return; } } static struct cmd gif_cmds[] = { DEF_CMD("ignore_source", GIF_IGNORE_SOURCE, setgifopts), DEF_CMD("-ignore_source", -GIF_IGNORE_SOURCE, setgifopts), }; static struct afswtch af_gif = { .af_name = "af_gif", .af_af = AF_UNSPEC, .af_other_status = gif_status, }; static __constructor void gif_ctor(void) { size_t i; for (i = 0; i < nitems(gif_cmds); i++) cmd_register(&gif_cmds[i]); af_register(&af_gif); } diff --git a/sbin/ifconfig/ifgroup.c b/sbin/ifconfig/ifgroup.c index 702e4732db7c..49cce678bb5e 100644 --- a/sbin/ifconfig/ifgroup.c +++ b/sbin/ifconfig/ifgroup.c @@ -1,169 +1,164 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2006 Max Laier. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" static void setifgroup(if_ctx *ctx, const char *group_name, int dummy __unused) { struct ifgroupreq ifgr = {}; strlcpy(ifgr.ifgr_name, ctx->ifname, IFNAMSIZ); if (group_name[0] && isdigit(group_name[strlen(group_name) - 1])) errx(1, "setifgroup: group names may not end in a digit"); if (strlcpy(ifgr.ifgr_group, group_name, IFNAMSIZ) >= IFNAMSIZ) errx(1, "setifgroup: group name too long"); if (ioctl_ctx(ctx, SIOCAIFGROUP, (caddr_t)&ifgr) == -1 && errno != EEXIST) err(1," SIOCAIFGROUP"); } static void unsetifgroup(if_ctx *ctx, const char *group_name, int dummy __unused) { struct ifgroupreq ifgr = {}; strlcpy(ifgr.ifgr_name, ctx->ifname, IFNAMSIZ); if (group_name[0] && isdigit(group_name[strlen(group_name) - 1])) errx(1, "unsetifgroup: group names may not end in a digit"); if (strlcpy(ifgr.ifgr_group, group_name, IFNAMSIZ) >= IFNAMSIZ) errx(1, "unsetifgroup: group name too long"); if (ioctl_ctx(ctx, SIOCDIFGROUP, (caddr_t)&ifgr) == -1 && errno != ENOENT) err(1, "SIOCDIFGROUP"); } static void getifgroups(if_ctx *ctx) { struct ifgroupreq ifgr; size_t cnt; if (ifconfig_get_groups(lifh, ctx->ifname, &ifgr) == -1) return; cnt = 0; for (size_t i = 0; i < ifgr.ifgr_len / sizeof(struct ifg_req); ++i) { struct ifg_req *ifg = &ifgr.ifgr_groups[i]; if (strcmp(ifg->ifgrq_group, "all")) { if (cnt == 0) printf("\tgroups:"); cnt++; printf(" %s", ifg->ifgrq_group); } } if (cnt) printf("\n"); free(ifgr.ifgr_groups); } static void printgroup(const char *groupname) { struct ifgroupreq ifgr; struct ifg_req *ifg; unsigned int len; int s; s = socket(AF_LOCAL, SOCK_DGRAM, 0); if (s == -1) err(1, "socket(AF_LOCAL,SOCK_DGRAM)"); bzero(&ifgr, sizeof(ifgr)); strlcpy(ifgr.ifgr_name, groupname, sizeof(ifgr.ifgr_name)); if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1) { if (errno == EINVAL || errno == ENOTTY || errno == ENOENT) exit(exit_code); else err(1, "SIOCGIFGMEMB"); } len = ifgr.ifgr_len; if ((ifgr.ifgr_groups = calloc(1, len)) == NULL) err(1, "printgroup"); if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1) err(1, "SIOCGIFGMEMB"); for (ifg = ifgr.ifgr_groups; ifg && len >= sizeof(struct ifg_req); ifg++) { len -= sizeof(struct ifg_req); printf("%s\n", ifg->ifgrq_member); } free(ifgr.ifgr_groups); exit(exit_code); } static struct cmd group_cmds[] = { DEF_CMD_ARG("group", setifgroup), DEF_CMD_ARG("-group", unsetifgroup), }; static struct afswtch af_group = { .af_name = "af_group", .af_af = AF_UNSPEC, .af_other_status = getifgroups, }; static struct option group_gopt = { .opt = "g:", .opt_usage = "[-g groupname]", .cb = printgroup, }; static __constructor void group_ctor(void) { for (size_t i = 0; i < nitems(group_cmds); i++) cmd_register(&group_cmds[i]); af_register(&af_group); opt_register(&group_gopt); } diff --git a/sbin/ifconfig/iflagg.c b/sbin/ifconfig/iflagg.c index 9d148c314119..4de437d25bd9 100644 --- a/sbin/ifconfig/iflagg.c +++ b/sbin/ifconfig/iflagg.c @@ -1,347 +1,342 @@ /*- */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" static struct iflaggparam params = { .lagg_type = LAGG_TYPE_DEFAULT, }; static char lacpbuf[120]; /* LACP peer '[(a,a,a),(p,p,p)]' */ static void setlaggport(if_ctx *ctx, const char *val, int dummy __unused) { struct lagg_reqport rp = {}; strlcpy(rp.rp_ifname, ctx->ifname, sizeof(rp.rp_ifname)); strlcpy(rp.rp_portname, val, sizeof(rp.rp_portname)); /* * Do not exit with an error here. Doing so permits a * failed NIC to take down an entire lagg. * * Don't error at all if the port is already in the lagg. */ if (ioctl_ctx(ctx, SIOCSLAGGPORT, &rp) && errno != EEXIST) { warnx("%s %s: SIOCSLAGGPORT: %s", ctx->ifname, val, strerror(errno)); exit_code = 1; } } static void unsetlaggport(if_ctx *ctx, const char *val, int dummy __unused) { struct lagg_reqport rp = {}; strlcpy(rp.rp_ifname, ctx->ifname, sizeof(rp.rp_ifname)); strlcpy(rp.rp_portname, val, sizeof(rp.rp_portname)); if (ioctl_ctx(ctx, SIOCSLAGGDELPORT, &rp)) err(1, "SIOCSLAGGDELPORT"); } static void setlaggproto(if_ctx *ctx, const char *val, int dummy __unused) { struct lagg_protos lpr[] = LAGG_PROTOS; struct lagg_reqall ra; bzero(&ra, sizeof(ra)); ra.ra_proto = LAGG_PROTO_MAX; for (size_t i = 0; i < nitems(lpr); i++) { if (strcmp(val, lpr[i].lpr_name) == 0) { ra.ra_proto = lpr[i].lpr_proto; break; } } if (ra.ra_proto == LAGG_PROTO_MAX) errx(1, "Invalid aggregation protocol: %s", val); strlcpy(ra.ra_ifname, ctx->ifname, sizeof(ra.ra_ifname)); if (ioctl_ctx(ctx, SIOCSLAGG, &ra) != 0) err(1, "SIOCSLAGG"); } static void setlaggflowidshift(if_ctx *ctx, const char *val, int dummy __unused) { struct lagg_reqopts ro = {}; ro.ro_opts = LAGG_OPT_FLOWIDSHIFT; strlcpy(ro.ro_ifname, ctx->ifname, sizeof(ro.ro_ifname)); ro.ro_flowid_shift = (int)strtol(val, NULL, 10); if (ro.ro_flowid_shift & ~LAGG_OPT_FLOWIDSHIFT_MASK) errx(1, "Invalid flowid_shift option: %s", val); if (ioctl_ctx(ctx, SIOCSLAGGOPTS, &ro) != 0) err(1, "SIOCSLAGGOPTS"); } static void setlaggrr_limit(if_ctx *ctx, const char *val, int dummy __unused) { struct lagg_reqopts ro = {}; strlcpy(ro.ro_ifname, ctx->ifname, sizeof(ro.ro_ifname)); ro.ro_opts = LAGG_OPT_RR_LIMIT; ro.ro_bkt = (uint32_t)strtoul(val, NULL, 10); if (ro.ro_bkt == 0) errx(1, "Invalid round-robin stride: %s", val); if (ioctl_ctx(ctx, SIOCSLAGGOPTS, &ro) != 0) err(1, "SIOCSLAGGOPTS"); } static void setlaggsetopt(if_ctx *ctx, const char *val __unused, int d) { struct lagg_reqopts ro = {}; ro.ro_opts = d; switch (ro.ro_opts) { case LAGG_OPT_USE_FLOWID: case -LAGG_OPT_USE_FLOWID: case LAGG_OPT_USE_NUMA: case -LAGG_OPT_USE_NUMA: case LAGG_OPT_LACP_STRICT: case -LAGG_OPT_LACP_STRICT: case LAGG_OPT_LACP_TXTEST: case -LAGG_OPT_LACP_TXTEST: case LAGG_OPT_LACP_RXTEST: case -LAGG_OPT_LACP_RXTEST: case LAGG_OPT_LACP_FAST_TIMO: case -LAGG_OPT_LACP_FAST_TIMO: break; default: err(1, "Invalid lagg option"); } strlcpy(ro.ro_ifname, ctx->ifname, sizeof(ro.ro_ifname)); if (ioctl_ctx(ctx, SIOCSLAGGOPTS, &ro) != 0) err(1, "SIOCSLAGGOPTS"); } static void setlagghash(if_ctx *ctx, const char *val, int dummy __unused) { struct lagg_reqflags rf; char *str, *tmp, *tok; rf.rf_flags = 0; str = tmp = strdup(val); while ((tok = strsep(&tmp, ",")) != NULL) { if (strcmp(tok, "l2") == 0) rf.rf_flags |= LAGG_F_HASHL2; else if (strcmp(tok, "l3") == 0) rf.rf_flags |= LAGG_F_HASHL3; else if (strcmp(tok, "l4") == 0) rf.rf_flags |= LAGG_F_HASHL4; else errx(1, "Invalid lagghash option: %s", tok); } free(str); if (rf.rf_flags == 0) errx(1, "No lagghash options supplied"); strlcpy(rf.rf_ifname, ctx->ifname, sizeof(rf.rf_ifname)); if (ioctl_ctx(ctx, SIOCSLAGGHASH, &rf)) err(1, "SIOCSLAGGHASH"); } static char * lacp_format_mac(const uint8_t *mac, char *buf, size_t buflen) { snprintf(buf, buflen, "%02X-%02X-%02X-%02X-%02X-%02X", (int)mac[0], (int)mac[1], (int)mac[2], (int)mac[3], (int)mac[4], (int)mac[5]); return (buf); } static char * lacp_format_peer(struct lacp_opreq *req, const char *sep) { char macbuf1[20]; char macbuf2[20]; snprintf(lacpbuf, sizeof(lacpbuf), "[(%04X,%s,%04X,%04X,%04X),%s(%04X,%s,%04X,%04X,%04X)]", req->actor_prio, lacp_format_mac(req->actor_mac, macbuf1, sizeof(macbuf1)), req->actor_key, req->actor_portprio, req->actor_portno, sep, req->partner_prio, lacp_format_mac(req->partner_mac, macbuf2, sizeof(macbuf2)), req->partner_key, req->partner_portprio, req->partner_portno); return(lacpbuf); } static void lagg_status(if_ctx *ctx) { struct lagg_protos protos[] = LAGG_PROTOS; struct ifconfig_lagg_status *lagg; struct lagg_reqall *ra; struct lagg_reqflags *rf; struct lagg_reqopts *ro; struct lagg_reqport *ports; struct lacp_opreq *lp; const char *proto; const int verbose = ctx->args->verbose; if (ifconfig_lagg_get_lagg_status(lifh, ctx->ifname, &lagg) == -1) return; ra = lagg->ra; rf = lagg->rf; ro = lagg->ro; ports = ra->ra_port; proto = ""; for (size_t i = 0; i < nitems(protos); ++i) { if (ra->ra_proto == protos[i].lpr_proto) { proto = protos[i].lpr_name; break; } } printf("\tlaggproto %s", proto); if (rf->rf_flags & LAGG_F_HASHMASK) { const char *sep = ""; printf(" lagghash "); if (rf->rf_flags & LAGG_F_HASHL2) { printf("%sl2", sep); sep = ","; } if (rf->rf_flags & LAGG_F_HASHL3) { printf("%sl3", sep); sep = ","; } if (rf->rf_flags & LAGG_F_HASHL4) { printf("%sl4", sep); sep = ","; } } putchar('\n'); if (verbose) { printf("\tlagg options:\n"); printb("\t\tflags", ro->ro_opts, LAGG_OPT_BITS); putchar('\n'); printf("\t\tflowid_shift: %d\n", ro->ro_flowid_shift); if (ra->ra_proto == LAGG_PROTO_ROUNDROBIN) printf("\t\trr_limit: %d\n", ro->ro_bkt); printf("\tlagg statistics:\n"); printf("\t\tactive ports: %d\n", ro->ro_active); printf("\t\tflapping: %u\n", ro->ro_flapping); if (ra->ra_proto == LAGG_PROTO_LACP) { lp = &ra->ra_lacpreq; printf("\tlag id: %s\n", lacp_format_peer(lp, "\n\t\t ")); } } for (size_t i = 0; i < (size_t)ra->ra_ports; ++i) { lp = &ports[i].rp_lacpreq; printf("\tlaggport: %s ", ports[i].rp_portname); printb("flags", ports[i].rp_flags, LAGG_PORT_BITS); if (verbose && ra->ra_proto == LAGG_PROTO_LACP) printb(" state", lp->actor_state, LACP_STATE_BITS); putchar('\n'); if (verbose && ra->ra_proto == LAGG_PROTO_LACP) printf("\t\t%s\n", lacp_format_peer(lp, "\n\t\t ")); } ifconfig_lagg_free_lagg_status(lagg); } static void setlaggtype(if_ctx *ctx __unused, const char *arg, int dummy __unused) { static const struct lagg_types lt[] = LAGG_TYPES; for (size_t i = 0; i < nitems(lt); i++) { if (strcmp(arg, lt[i].lt_name) == 0) { params.lagg_type = lt[i].lt_value; return; } } errx(1, "invalid lagg type: %s", arg); } static void lagg_create(if_ctx *ctx, struct ifreq *ifr) { ifr->ifr_data = (caddr_t) ¶ms; ifcreate_ioctl(ctx, ifr); } static struct cmd lagg_cmds[] = { DEF_CLONE_CMD_ARG("laggtype", setlaggtype), DEF_CMD_ARG("laggport", setlaggport), DEF_CMD_ARG("-laggport", unsetlaggport), DEF_CMD_ARG("laggproto", setlaggproto), DEF_CMD_ARG("lagghash", setlagghash), DEF_CMD("use_flowid", LAGG_OPT_USE_FLOWID, setlaggsetopt), DEF_CMD("-use_flowid", -LAGG_OPT_USE_FLOWID, setlaggsetopt), DEF_CMD("use_numa", LAGG_OPT_USE_NUMA, setlaggsetopt), DEF_CMD("-use_numa", -LAGG_OPT_USE_NUMA, setlaggsetopt), DEF_CMD("lacp_strict", LAGG_OPT_LACP_STRICT, setlaggsetopt), DEF_CMD("-lacp_strict", -LAGG_OPT_LACP_STRICT, setlaggsetopt), DEF_CMD("lacp_txtest", LAGG_OPT_LACP_TXTEST, setlaggsetopt), DEF_CMD("-lacp_txtest", -LAGG_OPT_LACP_TXTEST, setlaggsetopt), DEF_CMD("lacp_rxtest", LAGG_OPT_LACP_RXTEST, setlaggsetopt), DEF_CMD("-lacp_rxtest", -LAGG_OPT_LACP_RXTEST, setlaggsetopt), DEF_CMD("lacp_fast_timeout", LAGG_OPT_LACP_FAST_TIMO, setlaggsetopt), DEF_CMD("-lacp_fast_timeout", -LAGG_OPT_LACP_FAST_TIMO, setlaggsetopt), DEF_CMD_ARG("flowid_shift", setlaggflowidshift), DEF_CMD_ARG("rr_limit", setlaggrr_limit), }; static struct afswtch af_lagg = { .af_name = "af_lagg", .af_af = AF_UNSPEC, .af_other_status = lagg_status, }; static __constructor void lagg_ctor(void) { for (size_t i = 0; i < nitems(lagg_cmds); i++) cmd_register(&lagg_cmds[i]); af_register(&af_lagg); clone_setdefcallback_prefix("lagg", lagg_create); } diff --git a/sbin/ifconfig/ifvlan.c b/sbin/ifconfig/ifvlan.c index 90854885b561..a79ea35bc14b 100644 --- a/sbin/ifconfig/ifvlan.c +++ b/sbin/ifconfig/ifvlan.c @@ -1,316 +1,311 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1999 Bill Paul * Copyright (c) 2012 ADARA Networks, Inc. * All rights reserved. * * Portions of this software were developed by Robert N. M. Watson under * contract to ADARA Networks, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif - #define NOTAG ((u_short) -1) static const char proto_8021Q[] = "802.1q"; static const char proto_8021ad[] = "802.1ad"; static const char proto_qinq[] = "qinq"; static struct vlanreq params = { .vlr_tag = NOTAG, .vlr_proto = ETHERTYPE_VLAN, }; static void vlan_status(if_ctx *ctx) { struct vlanreq vreq = {}; struct ifreq ifr = { .ifr_data = (caddr_t)&vreq }; if (ioctl_ctx_ifr(ctx, SIOCGETVLAN, &ifr) == -1) return; printf("\tvlan: %d", vreq.vlr_tag); printf(" vlanproto: "); switch (vreq.vlr_proto) { case ETHERTYPE_VLAN: printf(proto_8021Q); break; case ETHERTYPE_QINQ: printf(proto_8021ad); break; default: printf("0x%04x", vreq.vlr_proto); } if (ioctl_ctx_ifr(ctx, SIOCGVLANPCP, &ifr) != -1) printf(" vlanpcp: %u", ifr.ifr_vlan_pcp); printf(" parent interface: %s", vreq.vlr_parent[0] == '\0' ? "" : vreq.vlr_parent); printf("\n"); } static int vlan_match_ethervid(const char *name) { return (strchr(name, '.') != NULL); } static void vlan_parse_ethervid(const char *name) { char ifname[IFNAMSIZ]; char *cp; unsigned int vid; strlcpy(ifname, name, IFNAMSIZ); if ((cp = strrchr(ifname, '.')) == NULL) return; /* * Derive params from interface name: "parent.vid". */ *cp++ = '\0'; if ((*cp < '1') || (*cp > '9')) errx(1, "invalid vlan tag"); vid = *cp++ - '0'; while ((*cp >= '0') && (*cp <= '9')) { vid = (vid * 10) + (*cp++ - '0'); if (vid >= 0xFFF) errx(1, "invalid vlan tag"); } if (*cp != '\0') errx(1, "invalid vlan tag"); /* * allow "devX.Y vlandev devX vlan Y" syntax */ if (params.vlr_tag == NOTAG || params.vlr_tag == vid) params.vlr_tag = vid; else errx(1, "ambiguous vlan specification"); /* Restrict overriding interface name */ if (params.vlr_parent[0] == '\0' || !strcmp(params.vlr_parent, ifname)) strlcpy(params.vlr_parent, ifname, IFNAMSIZ); else errx(1, "ambiguous vlan specification"); } static void vlan_create(if_ctx *ctx, struct ifreq *ifr) { vlan_parse_ethervid(ifr->ifr_name); if (params.vlr_tag != NOTAG || params.vlr_parent[0] != '\0') { /* * One or both parameters were specified, make sure both. */ if (params.vlr_tag == NOTAG) errx(1, "must specify a tag for vlan create"); if (params.vlr_parent[0] == '\0') errx(1, "must specify a parent device for vlan create"); ifr->ifr_data = (caddr_t) ¶ms; } ifcreate_ioctl(ctx, ifr); } static void vlan_cb(if_ctx *ctx __unused, void *arg __unused) { if ((params.vlr_tag != NOTAG) ^ (params.vlr_parent[0] != '\0')) errx(1, "both vlan and vlandev must be specified"); } static void vlan_set(int s, struct ifreq *ifr) { if (params.vlr_tag != NOTAG && params.vlr_parent[0] != '\0') { ifr->ifr_data = (caddr_t) ¶ms; if (ioctl(s, SIOCSETVLAN, (caddr_t)ifr) == -1) err(1, "SIOCSETVLAN"); } } static void setvlantag(if_ctx *ctx, const char *val, int dummy __unused) { struct vlanreq vreq = {}; struct ifreq ifr = { .ifr_data = (caddr_t)&vreq }; u_long ul; char *endp; ul = strtoul(val, &endp, 0); if (*endp != '\0') errx(1, "invalid value for vlan"); params.vlr_tag = ul; /* check if the value can be represented in vlr_tag */ if (params.vlr_tag != ul) errx(1, "value for vlan out of range"); if (ioctl_ctx_ifr(ctx, SIOCGETVLAN, &ifr) != -1) { vreq.vlr_tag = params.vlr_tag; memcpy(¶ms, &vreq, sizeof(params)); vlan_set(ctx->io_s, &ifr); } } static void setvlandev(if_ctx *ctx, const char *val, int dummy __unused) { struct vlanreq vreq = {}; struct ifreq ifr = { .ifr_data = (caddr_t)&vreq }; strlcpy(params.vlr_parent, val, sizeof(params.vlr_parent)); if (ioctl_ctx_ifr(ctx, SIOCGETVLAN, &ifr) != -1) vlan_set(ctx->io_s, &ifr); } static void setvlanproto(if_ctx *ctx, const char *val, int dummy __unused) { struct vlanreq vreq = {}; struct ifreq ifr = { .ifr_data = (caddr_t)&vreq }; if (strncasecmp(proto_8021Q, val, strlen(proto_8021Q)) == 0) { params.vlr_proto = ETHERTYPE_VLAN; } else if ((strncasecmp(proto_8021ad, val, strlen(proto_8021ad)) == 0) || (strncasecmp(proto_qinq, val, strlen(proto_qinq)) == 0)) { params.vlr_proto = ETHERTYPE_QINQ; } else errx(1, "invalid value for vlanproto"); if (ioctl_ctx_ifr(ctx, SIOCGETVLAN, &ifr) != -1) { vreq.vlr_proto = params.vlr_proto; memcpy(¶ms, &vreq, sizeof(params)); vlan_set(ctx->io_s, &ifr); } } static void setvlanpcp(if_ctx *ctx, const char *val, int dummy __unused) { u_long ul; char *endp; struct ifreq ifr = {}; ul = strtoul(val, &endp, 0); if (*endp != '\0') errx(1, "invalid value for vlanpcp"); if (ul > 7) errx(1, "value for vlanpcp out of range"); ifr.ifr_vlan_pcp = ul; if (ioctl_ctx_ifr(ctx, SIOCSVLANPCP, &ifr) == -1) err(1, "SIOCSVLANPCP"); } static void unsetvlandev(if_ctx *ctx, const char *val __unused, int dummy __unused) { struct vlanreq vreq = {}; struct ifreq ifr = { .ifr_data = (caddr_t)&vreq }; if (ioctl_ctx_ifr(ctx, SIOCGETVLAN, &ifr) == -1) err(1, "SIOCGETVLAN"); bzero((char *)&vreq.vlr_parent, sizeof(vreq.vlr_parent)); vreq.vlr_tag = 0; if (ioctl_ctx(ctx, SIOCSETVLAN, (caddr_t)&ifr) == -1) err(1, "SIOCSETVLAN"); } static struct cmd vlan_cmds[] = { DEF_CLONE_CMD_ARG("vlan", setvlantag), DEF_CLONE_CMD_ARG("vlandev", setvlandev), DEF_CLONE_CMD_ARG("vlanproto", setvlanproto), DEF_CMD_ARG("vlanpcp", setvlanpcp), /* NB: non-clone cmds */ DEF_CMD_ARG("vlan", setvlantag), DEF_CMD_ARG("vlandev", setvlandev), DEF_CMD_ARG("vlanproto", setvlanproto), /* XXX For compatibility. Should become DEF_CMD() some day. */ DEF_CMD_OPTARG("-vlandev", unsetvlandev), DEF_CMD("vlanmtu", IFCAP_VLAN_MTU, setifcap), DEF_CMD("-vlanmtu", IFCAP_VLAN_MTU, clearifcap), DEF_CMD("vlanhwtag", IFCAP_VLAN_HWTAGGING, setifcap), DEF_CMD("-vlanhwtag", IFCAP_VLAN_HWTAGGING, clearifcap), DEF_CMD("vlanhwfilter", IFCAP_VLAN_HWFILTER, setifcap), DEF_CMD("-vlanhwfilter", IFCAP_VLAN_HWFILTER, clearifcap), DEF_CMD("vlanhwtso", IFCAP_VLAN_HWTSO, setifcap), DEF_CMD("-vlanhwtso", IFCAP_VLAN_HWTSO, clearifcap), DEF_CMD("vlanhwcsum", IFCAP_VLAN_HWCSUM, setifcap), DEF_CMD("-vlanhwcsum", IFCAP_VLAN_HWCSUM, clearifcap), }; static struct afswtch af_vlan = { .af_name = "af_vlan", .af_af = AF_UNSPEC, .af_other_status = vlan_status, }; static __constructor void vlan_ctor(void) { size_t i; for (i = 0; i < nitems(vlan_cmds); i++) cmd_register(&vlan_cmds[i]); af_register(&af_vlan); callback_register(vlan_cb, NULL); clone_setdefcallback_prefix("vlan", vlan_create); clone_setdefcallback_filter(vlan_match_ethervid, vlan_create); } diff --git a/sbin/ifconfig/sfp.c b/sbin/ifconfig/sfp.c index a357a2bbefdd..0dc1def751b1 100644 --- a/sbin/ifconfig/sfp.c +++ b/sbin/ifconfig/sfp.c @@ -1,132 +1,127 @@ /*- * Copyright (c) 2014 Alexander V. Chernikov. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" void sfp_status(if_ctx *ctx) { struct ifconfig_sfp_info info; struct ifconfig_sfp_info_strings strings; struct ifconfig_sfp_vendor_info vendor_info; struct ifconfig_sfp_status status; size_t channel_count; int verbose = ctx->args->verbose; if (ifconfig_sfp_get_sfp_info(lifh, ctx->ifname, &info) == -1) return; ifconfig_sfp_get_sfp_info_strings(&info, &strings); printf("\tplugged: %s %s (%s)\n", ifconfig_sfp_id_display(info.sfp_id), ifconfig_sfp_physical_spec(&info, &strings), strings.sfp_conn); if (ifconfig_sfp_get_sfp_vendor_info(lifh, ctx->ifname, &vendor_info) == -1) return; printf("\tvendor: %s PN: %s SN: %s DATE: %s\n", vendor_info.name, vendor_info.pn, vendor_info.sn, vendor_info.date); if (ifconfig_sfp_id_is_qsfp(info.sfp_id)) { if (verbose > 1) printf("\tcompliance level: %s\n", strings.sfp_rev); } else { if (verbose > 5) { printf("Class: %s\n", ifconfig_sfp_physical_spec(&info, &strings)); printf("Length: %s\n", strings.sfp_fc_len); printf("Tech: %s\n", strings.sfp_cab_tech); printf("Media: %s\n", strings.sfp_fc_media); printf("Speed: %s\n", strings.sfp_fc_speed); } } if (ifconfig_sfp_get_sfp_status(lifh, ctx->ifname, &status) == 0) { if (ifconfig_sfp_id_is_qsfp(info.sfp_id) && verbose > 1) printf("\tnominal bitrate: %u Mbps\n", status.bitrate); printf("\tmodule temperature: %.2f C voltage: %.2f Volts\n", status.temp, status.voltage); channel_count = ifconfig_sfp_channel_count(&info); for (size_t chan = 0; chan < channel_count; ++chan) { uint16_t rx = status.channel[chan].rx; uint16_t tx = status.channel[chan].tx; printf("\tlane %zu: " "RX power: %.2f mW (%.2f dBm) TX bias: %.2f mA\n", chan + 1, power_mW(rx), power_dBm(rx), bias_mA(tx)); } ifconfig_sfp_free_sfp_status(&status); } if (verbose > 2) { struct ifconfig_sfp_dump dump; if (ifconfig_sfp_get_sfp_dump(lifh, ctx->ifname, &dump) == -1) return; if (ifconfig_sfp_id_is_qsfp(info.sfp_id)) { printf("\n\tSFF8436 DUMP (0xA0 128..255 range):\n"); hexdump(dump.data + QSFP_DUMP1_START, QSFP_DUMP1_SIZE, "\t", HD_OMIT_COUNT | HD_OMIT_CHARS); printf("\n\tSFF8436 DUMP (0xA0 0..81 range):\n"); hexdump(dump.data + QSFP_DUMP0_START, QSFP_DUMP0_SIZE, "\t", HD_OMIT_COUNT | HD_OMIT_CHARS); } else { printf("\n\tSFF8472 DUMP (0xA0 0..127 range):\n"); hexdump(dump.data + SFP_DUMP_START, SFP_DUMP_SIZE, "\t", HD_OMIT_COUNT | HD_OMIT_CHARS); } } } diff --git a/sbin/init/init.c b/sbin/init/init.c index dd8800d21910..d5200e4cfe35 100644 --- a/sbin/init/init.c +++ b/sbin/init/init.c @@ -1,2169 +1,2167 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Donn Seeley at Berkeley Software Design, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1991, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)init.c 8.1 (Berkeley) 7/15/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SECURE #include #endif #ifdef LOGIN_CAP #include #endif #include "mntopts.h" #include "pathnames.h" /* * Sleep times; used to prevent thrashing. */ #define GETTY_SPACING 5 /* N secs minimum getty spacing */ #define GETTY_SLEEP 30 /* sleep N secs after spacing problem */ #define GETTY_NSPACE 3 /* max. spacing count to bring reaction */ #define WINDOW_WAIT 3 /* wait N secs after starting window */ #define STALL_TIMEOUT 30 /* wait N secs after warning */ #define DEATH_WATCH 10 /* wait N secs for procs to die */ #define DEATH_SCRIPT 120 /* wait for 2min for /etc/rc.shutdown */ #define RESOURCE_RC "daemon" #define RESOURCE_WINDOW "default" #define RESOURCE_GETTY "default" #define SCRIPT_ARGV_SIZE 3 /* size of argv passed to execute_script, can be increased if needed */ static void handle(sig_t, ...); static void delset(sigset_t *, ...); static void stall(const char *, ...) __printflike(1, 2); static void warning(const char *, ...) __printflike(1, 2); static void emergency(const char *, ...) __printflike(1, 2); static void disaster(int); static void revoke_ttys(void); static int runshutdown(void); static char *strk(char *); static void runfinal(void); /* * We really need a recursive typedef... * The following at least guarantees that the return type of (*state_t)() * is sufficiently wide to hold a function pointer. */ typedef long (*state_func_t)(void); typedef state_func_t (*state_t)(void); static state_func_t single_user(void); static state_func_t runcom(void); static state_func_t read_ttys(void); static state_func_t multi_user(void); static state_func_t clean_ttys(void); static state_func_t catatonia(void); static state_func_t death(void); static state_func_t death_single(void); static state_func_t reroot(void); static state_func_t reroot_phase_two(void); static state_func_t run_script(const char *); static enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT; static bool Reboot = false; static int howto = RB_AUTOBOOT; static bool devfs = false; static char *init_path_argv0; static void transition(state_t); static state_t requested_transition; static state_t current_state = death_single; static void execute_script(char *argv[]); static void open_console(void); static const char *get_shell(void); static void replace_init(char *path); static void write_stderr(const char *message); typedef struct init_session { pid_t se_process; /* controlling process */ time_t se_started; /* used to avoid thrashing */ int se_flags; /* status of session */ #define SE_SHUTDOWN 0x1 /* session won't be restarted */ #define SE_PRESENT 0x2 /* session is in /etc/ttys */ #define SE_IFEXISTS 0x4 /* session defined as "onifexists" */ #define SE_IFCONSOLE 0x8 /* session defined as "onifconsole" */ int se_nspace; /* spacing count */ char *se_device; /* filename of port */ char *se_getty; /* what to run on that port */ char *se_getty_argv_space; /* pre-parsed argument array space */ char **se_getty_argv; /* pre-parsed argument array */ char *se_window; /* window system (started only once) */ char *se_window_argv_space; /* pre-parsed argument array space */ char **se_window_argv; /* pre-parsed argument array */ char *se_type; /* default terminal type */ struct init_session *se_prev; struct init_session *se_next; } session_t; static void free_session(session_t *); static session_t *new_session(session_t *, struct ttyent *); static session_t *sessions; static char **construct_argv(char *); static void start_window_system(session_t *); static void collect_child(pid_t); static pid_t start_getty(session_t *); static void transition_handler(int); static void alrm_handler(int); static void setsecuritylevel(int); static int getsecuritylevel(void); static int setupargv(session_t *, struct ttyent *); #ifdef LOGIN_CAP static void setprocresources(const char *); #endif static bool clang; static int start_session_db(void); static void add_session(session_t *); static void del_session(session_t *); static session_t *find_session(pid_t); static DB *session_db; /* * The mother of all processes. */ int main(int argc, char *argv[]) { state_t initial_transition = runcom; char kenv_value[PATH_MAX]; int c, error; struct sigaction sa; sigset_t mask; /* Dispose of random users. */ if (getuid() != 0) errx(1, "%s", strerror(EPERM)); BOOTTRACE("init(8) starting..."); /* System V users like to reexec init. */ if (getpid() != 1) { #ifdef COMPAT_SYSV_INIT /* So give them what they want */ if (argc > 1) { if (strlen(argv[1]) == 1) { char runlevel = *argv[1]; int sig; switch (runlevel) { case '0': /* halt + poweroff */ sig = SIGUSR2; break; case '1': /* single-user */ sig = SIGTERM; break; case '6': /* reboot */ sig = SIGINT; break; case 'c': /* block further logins */ sig = SIGTSTP; break; case 'q': /* rescan /etc/ttys */ sig = SIGHUP; break; case 'r': /* remount root */ sig = SIGEMT; break; default: goto invalid; } kill(1, sig); _exit(0); } else invalid: errx(1, "invalid run-level ``%s''", argv[1]); } else #endif errx(1, "already running"); } init_path_argv0 = strdup(argv[0]); if (init_path_argv0 == NULL) err(1, "strdup"); /* * Note that this does NOT open a file... * Does 'init' deserve its own facility number? */ openlog("init", LOG_CONS, LOG_AUTH); /* * Create an initial session. */ if (setsid() < 0 && (errno != EPERM || getsid(0) != 1)) warning("initial setsid() failed: %m"); /* * Establish an initial user so that programs running * single user do not freak out and die (like passwd). */ if (setlogin("root") < 0) warning("setlogin() failed: %m"); /* * This code assumes that we always get arguments through flags, * never through bits set in some random machine register. */ while ((c = getopt(argc, argv, "dsfr")) != -1) switch (c) { case 'd': devfs = true; break; case 's': initial_transition = single_user; break; case 'f': runcom_mode = FASTBOOT; break; case 'r': initial_transition = reroot_phase_two; break; default: warning("unrecognized flag '-%c'", c); break; } if (optind != argc) warning("ignoring excess arguments"); /* * We catch or block signals rather than ignore them, * so that they get reset on exec. */ handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, SIGXCPU, SIGXFSZ, 0); handle(transition_handler, SIGHUP, SIGINT, SIGEMT, SIGTERM, SIGTSTP, SIGUSR1, SIGUSR2, SIGWINCH, 0); handle(alrm_handler, SIGALRM, 0); sigfillset(&mask); delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, SIGXCPU, SIGXFSZ, SIGHUP, SIGINT, SIGEMT, SIGTERM, SIGTSTP, SIGALRM, SIGUSR1, SIGUSR2, SIGWINCH, 0); sigprocmask(SIG_SETMASK, &mask, NULL); sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = SIG_IGN; sigaction(SIGTTIN, &sa, NULL); sigaction(SIGTTOU, &sa, NULL); /* * Paranoia. */ close(0); close(1); close(2); if (kenv(KENV_GET, "init_exec", kenv_value, sizeof(kenv_value)) > 0) { replace_init(kenv_value); _exit(0); /* reboot */ } if (kenv(KENV_GET, "init_script", kenv_value, sizeof(kenv_value)) > 0) { state_func_t next_transition; if ((next_transition = run_script(kenv_value)) != NULL) initial_transition = (state_t) next_transition; } if (kenv(KENV_GET, "init_chroot", kenv_value, sizeof(kenv_value)) > 0) { if (chdir(kenv_value) != 0 || chroot(".") != 0) warning("Can't chroot to %s: %m", kenv_value); } /* * Additional check if devfs needs to be mounted: * If "/" and "/dev" have the same device number, * then it hasn't been mounted yet. */ if (!devfs) { struct stat stst; dev_t root_devno; stat("/", &stst); root_devno = stst.st_dev; if (stat("/dev", &stst) != 0) warning("Can't stat /dev: %m"); else if (stst.st_dev == root_devno) devfs = true; } if (devfs) { struct iovec iov[4]; char *s; int i; char _fstype[] = "fstype"; char _devfs[] = "devfs"; char _fspath[] = "fspath"; char _path_dev[]= _PATH_DEV; iov[0].iov_base = _fstype; iov[0].iov_len = sizeof(_fstype); iov[1].iov_base = _devfs; iov[1].iov_len = sizeof(_devfs); iov[2].iov_base = _fspath; iov[2].iov_len = sizeof(_fspath); /* * Try to avoid the trailing slash in _PATH_DEV. * Be *very* defensive. */ s = strdup(_PATH_DEV); if (s != NULL) { i = strlen(s); if (i > 0 && s[i - 1] == '/') s[i - 1] = '\0'; iov[3].iov_base = s; iov[3].iov_len = strlen(s) + 1; } else { iov[3].iov_base = _path_dev; iov[3].iov_len = sizeof(_path_dev); } nmount(iov, 4, 0); if (s != NULL) free(s); } if (initial_transition != reroot_phase_two) { /* * Unmount reroot leftovers. This runs after init(8) * gets reexecuted after reroot_phase_two() is done. */ error = unmount(_PATH_REROOT, MNT_FORCE); if (error != 0 && errno != EINVAL) warning("Cannot unmount %s: %m", _PATH_REROOT); } /* * Start the state machine. */ transition(initial_transition); /* * Should never reach here. */ return 1; } /* * Associate a function with a signal handler. */ static void handle(sig_t handler, ...) { int sig; struct sigaction sa; sigset_t mask_everything; va_list ap; va_start(ap, handler); sa.sa_handler = handler; sigfillset(&mask_everything); while ((sig = va_arg(ap, int)) != 0) { sa.sa_mask = mask_everything; /* XXX SA_RESTART? */ sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0; sigaction(sig, &sa, NULL); } va_end(ap); } /* * Delete a set of signals from a mask. */ static void delset(sigset_t *maskp, ...) { int sig; va_list ap; va_start(ap, maskp); while ((sig = va_arg(ap, int)) != 0) sigdelset(maskp, sig); va_end(ap); } /* * Log a message and sleep for a while (to give someone an opportunity * to read it and to save log or hardcopy output if the problem is chronic). * NB: should send a message to the session logger to avoid blocking. */ static void stall(const char *message, ...) { va_list ap; va_start(ap, message); vsyslog(LOG_ALERT, message, ap); va_end(ap); sleep(STALL_TIMEOUT); } /* * Like stall(), but doesn't sleep. * If cpp had variadic macros, the two functions could be #defines for another. * NB: should send a message to the session logger to avoid blocking. */ static void warning(const char *message, ...) { va_list ap; va_start(ap, message); vsyslog(LOG_ALERT, message, ap); va_end(ap); } /* * Log an emergency message. * NB: should send a message to the session logger to avoid blocking. */ static void emergency(const char *message, ...) { va_list ap; va_start(ap, message); vsyslog(LOG_EMERG, message, ap); va_end(ap); } /* * Catch an unexpected signal. */ static void disaster(int sig) { emergency("fatal signal: %s", (unsigned)sig < NSIG ? sys_siglist[sig] : "unknown signal"); sleep(STALL_TIMEOUT); _exit(sig); /* reboot */ } /* * Get the security level of the kernel. */ static int getsecuritylevel(void) { #ifdef KERN_SECURELVL int name[2], curlevel; size_t len; name[0] = CTL_KERN; name[1] = KERN_SECURELVL; len = sizeof curlevel; if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) { emergency("cannot get kernel security level: %m"); return (-1); } return (curlevel); #else return (-1); #endif } /* * Set the security level of the kernel. */ static void setsecuritylevel(int newlevel) { #ifdef KERN_SECURELVL int name[2], curlevel; curlevel = getsecuritylevel(); if (newlevel == curlevel) return; name[0] = CTL_KERN; name[1] = KERN_SECURELVL; if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) { emergency( "cannot change kernel security level from %d to %d: %m", curlevel, newlevel); return; } #ifdef SECURE warning("kernel security level changed from %d to %d", curlevel, newlevel); #endif #endif } /* * Change states in the finite state machine. * The initial state is passed as an argument. */ static void transition(state_t s) { current_state = s; for (;;) current_state = (state_t) (*current_state)(); } /* * Start a session and allocate a controlling terminal. * Only called by children of init after forking. */ static void open_console(void) { int fd; /* * Try to open /dev/console. Open the device with O_NONBLOCK to * prevent potential blocking on a carrier. */ revoke(_PATH_CONSOLE); if ((fd = open(_PATH_CONSOLE, O_RDWR | O_NONBLOCK)) != -1) { (void)fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK); if (login_tty(fd) == 0) return; close(fd); } /* No luck. Log output to file if possible. */ if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1) { stall("cannot open null device."); _exit(1); } if (fd != STDIN_FILENO) { dup2(fd, STDIN_FILENO); close(fd); } fd = open(_PATH_INITLOG, O_WRONLY | O_APPEND | O_CREAT, 0644); if (fd == -1) dup2(STDIN_FILENO, STDOUT_FILENO); else if (fd != STDOUT_FILENO) { dup2(fd, STDOUT_FILENO); close(fd); } dup2(STDOUT_FILENO, STDERR_FILENO); } static const char * get_shell(void) { static char kenv_value[PATH_MAX]; if (kenv(KENV_GET, "init_shell", kenv_value, sizeof(kenv_value)) > 0) return kenv_value; else return _PATH_BSHELL; } static void write_stderr(const char *message) { write(STDERR_FILENO, message, strlen(message)); } static int read_file(const char *path, void **bufp, size_t *bufsizep) { struct stat sb; size_t bufsize; void *buf; ssize_t nbytes; int error, fd; fd = open(path, O_RDONLY); if (fd < 0) { emergency("%s: %m", path); return (-1); } error = fstat(fd, &sb); if (error != 0) { emergency("fstat: %m"); close(fd); return (error); } bufsize = sb.st_size; buf = malloc(bufsize); if (buf == NULL) { emergency("malloc: %m"); close(fd); return (error); } nbytes = read(fd, buf, bufsize); if (nbytes != (ssize_t)bufsize) { emergency("read: %m"); close(fd); free(buf); return (error); } error = close(fd); if (error != 0) { emergency("close: %m"); free(buf); return (error); } *bufp = buf; *bufsizep = bufsize; return (0); } static int create_file(const char *path, const void *buf, size_t bufsize) { ssize_t nbytes; int error, fd; fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0700); if (fd < 0) { emergency("%s: %m", path); return (-1); } nbytes = write(fd, buf, bufsize); if (nbytes != (ssize_t)bufsize) { emergency("write: %m"); close(fd); return (-1); } error = close(fd); if (error != 0) { emergency("close: %m"); return (-1); } return (0); } static int mount_tmpfs(const char *fspath) { struct iovec *iov; char errmsg[255]; int error, iovlen; iov = NULL; iovlen = 0; memset(errmsg, 0, sizeof(errmsg)); build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "tmpfs"), (size_t)-1); build_iovec(&iov, &iovlen, "fspath", __DECONST(void *, fspath), (size_t)-1); build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); error = nmount(iov, iovlen, 0); if (error != 0) { if (*errmsg != '\0') { emergency("cannot mount tmpfs on %s: %s: %m", fspath, errmsg); } else { emergency("cannot mount tmpfs on %s: %m", fspath); } return (error); } return (0); } static state_func_t reroot(void) { void *buf; size_t bufsize; int error; buf = NULL; bufsize = 0; revoke_ttys(); runshutdown(); /* * Make sure nobody can interfere with our scheme. * Ignore ESRCH, which can apparently happen when * there are no processes to kill. */ error = kill(-1, SIGKILL); if (error != 0 && errno != ESRCH) { emergency("kill(2) failed: %m"); goto out; } /* * Copy the init binary into tmpfs, so that we can unmount * the old rootfs without committing suicide. */ error = read_file(init_path_argv0, &buf, &bufsize); if (error != 0) goto out; error = mount_tmpfs(_PATH_REROOT); if (error != 0) goto out; error = create_file(_PATH_REROOT_INIT, buf, bufsize); if (error != 0) goto out; /* * Execute the temporary init. */ execl(_PATH_REROOT_INIT, _PATH_REROOT_INIT, "-r", NULL); emergency("cannot exec %s: %m", _PATH_REROOT_INIT); out: emergency("reroot failed; going to single user mode"); free(buf); return (state_func_t) single_user; } static state_func_t reroot_phase_two(void) { char init_path[PATH_MAX], *path, *path_component; size_t init_path_len; int nbytes, error; /* * Ask the kernel to mount the new rootfs. */ error = reboot(RB_REROOT); if (error != 0) { emergency("RB_REBOOT failed: %m"); goto out; } /* * Figure out where the destination init(8) binary is. Note that * the path could be different than what we've started with. Use * the value from kenv, if set, or the one from sysctl otherwise. * The latter defaults to a hardcoded value, but can be overridden * by a build time option. */ nbytes = kenv(KENV_GET, "init_path", init_path, sizeof(init_path)); if (nbytes <= 0) { init_path_len = sizeof(init_path); error = sysctlbyname("kern.init_path", init_path, &init_path_len, NULL, 0); if (error != 0) { emergency("failed to retrieve kern.init_path: %m"); goto out; } } /* * Repeat the init search logic from sys/kern/init_path.c */ path_component = init_path; while ((path = strsep(&path_component, ":")) != NULL) { /* * Execute init(8) from the new rootfs. */ execl(path, path, NULL); } emergency("cannot exec init from %s: %m", init_path); out: emergency("reroot failed; going to single user mode"); return (state_func_t) single_user; } /* * Bring the system up single user. */ static state_func_t single_user(void) { pid_t pid, wpid; int status; sigset_t mask; const char *shell; char *argv[2]; struct timeval tv, tn; #ifdef SECURE struct ttyent *typ; struct passwd *pp; static const char banner[] = "Enter root password, or ^D to go multi-user\n"; char *clear, *password; #endif #ifdef DEBUGSHELL char altshell[128]; #endif if (Reboot) { /* Instead of going single user, let's reboot the machine */ BOOTTRACE("shutting down the system"); sync(); /* Run scripts after all processes have been terminated. */ runfinal(); if (reboot(howto) == -1) { emergency("reboot(%#x) failed, %m", howto); _exit(1); /* panic and reboot */ } warning("reboot(%#x) returned", howto); _exit(0); /* panic as well */ } BOOTTRACE("going to single user mode"); shell = get_shell(); if ((pid = fork()) == 0) { /* * Start the single user session. */ open_console(); #ifdef SECURE /* * Check the root password. * We don't care if the console is 'on' by default; * it's the only tty that can be 'off' and 'secure'. */ typ = getttynam("console"); pp = getpwnam("root"); if (typ && (typ->ty_status & TTY_SECURE) == 0 && pp && *pp->pw_passwd) { write_stderr(banner); for (;;) { clear = getpass("Password:"); if (clear == NULL || *clear == '\0') _exit(0); password = crypt(clear, pp->pw_passwd); explicit_bzero(clear, _PASSWORD_LEN); if (password != NULL && strcmp(password, pp->pw_passwd) == 0) break; warning("single-user login failed\n"); } } endttyent(); endpwent(); #endif /* SECURE */ #ifdef DEBUGSHELL { char *cp = altshell; int num; #define SHREQUEST "Enter full pathname of shell or RETURN for " write_stderr(SHREQUEST); write_stderr(shell); write_stderr(": "); while ((num = read(STDIN_FILENO, cp, 1)) != -1 && num != 0 && *cp != '\n' && cp < &altshell[127]) cp++; *cp = '\0'; if (altshell[0] != '\0') shell = altshell; } #endif /* DEBUGSHELL */ /* * Unblock signals. * We catch all the interesting ones, * and those are reset to SIG_DFL on exec. */ sigemptyset(&mask); sigprocmask(SIG_SETMASK, &mask, NULL); /* * Fire off a shell. * If the default one doesn't work, try the Bourne shell. */ char name[] = "-sh"; argv[0] = name; argv[1] = NULL; execv(shell, argv); emergency("can't exec %s for single user: %m", shell); execv(_PATH_BSHELL, argv); emergency("can't exec %s for single user: %m", _PATH_BSHELL); sleep(STALL_TIMEOUT); _exit(1); } if (pid == -1) { /* * We are seriously hosed. Do our best. */ emergency("can't fork single-user shell, trying again"); while (waitpid(-1, (int *) 0, WNOHANG) > 0) continue; return (state_func_t) single_user; } requested_transition = 0; do { if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) collect_child(wpid); if (wpid == -1) { if (errno == EINTR) continue; warning("wait for single-user shell failed: %m; restarting"); return (state_func_t) single_user; } if (wpid == pid && WIFSTOPPED(status)) { warning("init: shell stopped, restarting\n"); kill(pid, SIGCONT); wpid = -1; } } while (wpid != pid && !requested_transition); if (requested_transition) return (state_func_t) requested_transition; if (!WIFEXITED(status)) { if (WTERMSIG(status) == SIGKILL) { /* * reboot(8) killed shell? */ warning("single user shell terminated."); gettimeofday(&tv, NULL); tn = tv; tv.tv_sec += STALL_TIMEOUT; while (tv.tv_sec > tn.tv_sec || (tv.tv_sec == tn.tv_sec && tv.tv_usec > tn.tv_usec)) { sleep(1); gettimeofday(&tn, NULL); } _exit(0); } else { warning("single user shell terminated, restarting"); return (state_func_t) single_user; } } runcom_mode = FASTBOOT; return (state_func_t) runcom; } /* * Run the system startup script. */ static state_func_t runcom(void) { state_func_t next_transition; BOOTTRACE("/etc/rc starting..."); if ((next_transition = run_script(_PATH_RUNCOM)) != NULL) return next_transition; BOOTTRACE("/etc/rc finished"); runcom_mode = AUTOBOOT; /* the default */ return (state_func_t) read_ttys; } static void execute_script(char *argv[]) { struct sigaction sa; char* sh_argv[3 + SCRIPT_ARGV_SIZE]; const char *shell, *script; int error, sh_argv_len, i; bzero(&sa, sizeof(sa)); sigemptyset(&sa.sa_mask); sa.sa_handler = SIG_IGN; sigaction(SIGTSTP, &sa, NULL); sigaction(SIGHUP, &sa, NULL); open_console(); sigprocmask(SIG_SETMASK, &sa.sa_mask, NULL); #ifdef LOGIN_CAP setprocresources(RESOURCE_RC); #endif /* * Try to directly execute the script first. If it * fails, try the old method of passing the script path * to sh(1). Don't complain if it fails because of * the missing execute bit. */ script = argv[0]; error = access(script, X_OK); if (error == 0) { execv(script, argv); warning("can't directly exec %s: %m", script); } else if (errno != EACCES) { warning("can't access %s: %m", script); } shell = get_shell(); sh_argv[0] = __DECONST(char*, shell); sh_argv_len = 1; #ifdef SECURE if (strcmp(shell, _PATH_BSHELL) == 0) { sh_argv[1] = __DECONST(char*, "-o"); sh_argv[2] = __DECONST(char*, "verify"); sh_argv_len = 3; } #endif for (i = 0; i != SCRIPT_ARGV_SIZE; ++i) sh_argv[i + sh_argv_len] = argv[i]; execv(shell, sh_argv); stall("can't exec %s for %s: %m", shell, script); } /* * Execute binary, replacing init(8) as PID 1. */ static void replace_init(char *path) { char *argv[SCRIPT_ARGV_SIZE]; argv[0] = path; argv[1] = NULL; execute_script(argv); } /* * Run a shell script. * Returns 0 on success, otherwise the next transition to enter: * - single_user if fork/execv/waitpid failed, or if the script * terminated with a signal or exit code != 0. * - death_single if a SIGTERM was delivered to init(8). */ static state_func_t run_script(const char *script) { pid_t pid, wpid; int status; char *argv[SCRIPT_ARGV_SIZE]; const char *shell; shell = get_shell(); if ((pid = fork()) == 0) { char _autoboot[] = "autoboot"; argv[0] = __DECONST(char *, script); argv[1] = runcom_mode == AUTOBOOT ? _autoboot : NULL; argv[2] = NULL; execute_script(argv); sleep(STALL_TIMEOUT); _exit(1); /* force single user mode */ } if (pid == -1) { emergency("can't fork for %s on %s: %m", shell, script); while (waitpid(-1, (int *) 0, WNOHANG) > 0) continue; sleep(STALL_TIMEOUT); return (state_func_t) single_user; } /* * Copied from single_user(). This is a bit paranoid. */ requested_transition = 0; do { if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) collect_child(wpid); if (requested_transition == death_single || requested_transition == reroot) return (state_func_t) requested_transition; if (wpid == -1) { if (errno == EINTR) continue; warning("wait for %s on %s failed: %m; going to " "single user mode", shell, script); return (state_func_t) single_user; } if (wpid == pid && WIFSTOPPED(status)) { warning("init: %s on %s stopped, restarting\n", shell, script); kill(pid, SIGCONT); wpid = -1; } } while (wpid != pid); if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && requested_transition == catatonia) { /* /etc/rc executed /sbin/reboot; wait for the end quietly */ sigset_t s; sigfillset(&s); for (;;) sigsuspend(&s); } if (!WIFEXITED(status)) { warning("%s on %s terminated abnormally, going to single " "user mode", shell, script); return (state_func_t) single_user; } if (WEXITSTATUS(status)) return (state_func_t) single_user; return (state_func_t) 0; } /* * Open the session database. * * NB: We could pass in the size here; is it necessary? */ static int start_session_db(void) { if (session_db && (*session_db->close)(session_db)) emergency("session database close: %m"); if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == NULL) { emergency("session database open: %m"); return (1); } return (0); } /* * Add a new login session. */ static void add_session(session_t *sp) { DBT key; DBT data; key.data = &sp->se_process; key.size = sizeof sp->se_process; data.data = &sp; data.size = sizeof sp; if ((*session_db->put)(session_db, &key, &data, 0)) emergency("insert %d: %m", sp->se_process); } /* * Delete an old login session. */ static void del_session(session_t *sp) { DBT key; key.data = &sp->se_process; key.size = sizeof sp->se_process; if ((*session_db->del)(session_db, &key, 0)) emergency("delete %d: %m", sp->se_process); } /* * Look up a login session by pid. */ static session_t * find_session(pid_t pid) { DBT key; DBT data; session_t *ret; key.data = &pid; key.size = sizeof pid; if ((*session_db->get)(session_db, &key, &data, 0) != 0) return 0; bcopy(data.data, (char *)&ret, sizeof(ret)); return ret; } /* * Construct an argument vector from a command line. */ static char ** construct_argv(char *command) { int argc = 0; char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1) * sizeof (char *)); if ((argv[argc++] = strk(command)) == NULL) { free(argv); return (NULL); } while ((argv[argc++] = strk((char *) 0)) != NULL) continue; return argv; } /* * Deallocate a session descriptor. */ static void free_session(session_t *sp) { free(sp->se_device); if (sp->se_getty) { free(sp->se_getty); free(sp->se_getty_argv_space); free(sp->se_getty_argv); } if (sp->se_window) { free(sp->se_window); free(sp->se_window_argv_space); free(sp->se_window_argv); } if (sp->se_type) free(sp->se_type); free(sp); } /* * Allocate a new session descriptor. * Mark it SE_PRESENT. */ static session_t * new_session(session_t *sprev, struct ttyent *typ) { session_t *sp; if ((typ->ty_status & TTY_ON) == 0 || typ->ty_name == 0 || typ->ty_getty == 0) return 0; sp = (session_t *) calloc(1, sizeof (session_t)); sp->se_flags |= SE_PRESENT; if ((typ->ty_status & TTY_IFEXISTS) != 0) sp->se_flags |= SE_IFEXISTS; if ((typ->ty_status & TTY_IFCONSOLE) != 0) sp->se_flags |= SE_IFCONSOLE; if (asprintf(&sp->se_device, "%s%s", _PATH_DEV, typ->ty_name) < 0) err(1, "asprintf"); if (setupargv(sp, typ) == 0) { free_session(sp); return (0); } sp->se_next = 0; if (sprev == NULL) { sessions = sp; sp->se_prev = 0; } else { sprev->se_next = sp; sp->se_prev = sprev; } return sp; } /* * Calculate getty and if useful window argv vectors. */ static int setupargv(session_t *sp, struct ttyent *typ) { if (sp->se_getty) { free(sp->se_getty); free(sp->se_getty_argv_space); free(sp->se_getty_argv); } if (asprintf(&sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name) < 0) err(1, "asprintf"); sp->se_getty_argv_space = strdup(sp->se_getty); sp->se_getty_argv = construct_argv(sp->se_getty_argv_space); if (sp->se_getty_argv == NULL) { warning("can't parse getty for port %s", sp->se_device); free(sp->se_getty); free(sp->se_getty_argv_space); sp->se_getty = sp->se_getty_argv_space = 0; return (0); } if (sp->se_window) { free(sp->se_window); free(sp->se_window_argv_space); free(sp->se_window_argv); } sp->se_window = sp->se_window_argv_space = 0; sp->se_window_argv = 0; if (typ->ty_window) { sp->se_window = strdup(typ->ty_window); sp->se_window_argv_space = strdup(sp->se_window); sp->se_window_argv = construct_argv(sp->se_window_argv_space); if (sp->se_window_argv == NULL) { warning("can't parse window for port %s", sp->se_device); free(sp->se_window_argv_space); free(sp->se_window); sp->se_window = sp->se_window_argv_space = 0; return (0); } } if (sp->se_type) free(sp->se_type); sp->se_type = typ->ty_type ? strdup(typ->ty_type) : 0; return (1); } /* * Walk the list of ttys and create sessions for each active line. */ static state_func_t read_ttys(void) { session_t *sp, *snext; struct ttyent *typ; /* * Destroy any previous session state. * There shouldn't be any, but just in case... */ for (sp = sessions; sp; sp = snext) { snext = sp->se_next; free_session(sp); } sessions = 0; if (start_session_db()) return (state_func_t) single_user; /* * Allocate a session entry for each active port. * Note that sp starts at 0. */ while ((typ = getttyent()) != NULL) if ((snext = new_session(sp, typ)) != NULL) sp = snext; endttyent(); return (state_func_t) multi_user; } /* * Start a window system running. */ static void start_window_system(session_t *sp) { pid_t pid; sigset_t mask; char term[64], *env[2]; int status; if ((pid = fork()) == -1) { emergency("can't fork for window system on port %s: %m", sp->se_device); /* hope that getty fails and we can try again */ return; } if (pid) { waitpid(-1, &status, 0); return; } /* reparent window process to the init to not make a zombie on exit */ if ((pid = fork()) == -1) { emergency("can't fork for window system on port %s: %m", sp->se_device); _exit(1); } if (pid) _exit(0); sigemptyset(&mask); sigprocmask(SIG_SETMASK, &mask, NULL); if (setsid() < 0) emergency("setsid failed (window) %m"); #ifdef LOGIN_CAP setprocresources(RESOURCE_WINDOW); #endif if (sp->se_type) { /* Don't use malloc after fork */ strcpy(term, "TERM="); strlcat(term, sp->se_type, sizeof(term)); env[0] = term; env[1] = NULL; } else env[0] = NULL; execve(sp->se_window_argv[0], sp->se_window_argv, env); stall("can't exec window system '%s' for port %s: %m", sp->se_window_argv[0], sp->se_device); _exit(1); } /* * Start a login session running. */ static pid_t start_getty(session_t *sp) { pid_t pid; sigset_t mask; time_t current_time = time((time_t *) 0); int too_quick = 0; char term[64], *env[2]; if (current_time >= sp->se_started && current_time - sp->se_started < GETTY_SPACING) { if (++sp->se_nspace > GETTY_NSPACE) { sp->se_nspace = 0; too_quick = 1; } } else sp->se_nspace = 0; /* * fork(), not vfork() -- we can't afford to block. */ if ((pid = fork()) == -1) { emergency("can't fork for getty on port %s: %m", sp->se_device); return -1; } if (pid) return pid; if (too_quick) { warning("getty repeating too quickly on port %s, sleeping %d secs", sp->se_device, GETTY_SLEEP); sleep((unsigned) GETTY_SLEEP); } if (sp->se_window) { start_window_system(sp); sleep(WINDOW_WAIT); } sigemptyset(&mask); sigprocmask(SIG_SETMASK, &mask, NULL); #ifdef LOGIN_CAP setprocresources(RESOURCE_GETTY); #endif if (sp->se_type) { /* Don't use malloc after fork */ strcpy(term, "TERM="); strlcat(term, sp->se_type, sizeof(term)); env[0] = term; env[1] = NULL; } else env[0] = NULL; execve(sp->se_getty_argv[0], sp->se_getty_argv, env); stall("can't exec getty '%s' for port %s: %m", sp->se_getty_argv[0], sp->se_device); _exit(1); } /* * Return 1 if the session is defined as "onifexists" * or "onifconsole" and the device node does not exist. */ static int session_has_no_tty(session_t *sp) { int fd; if ((sp->se_flags & SE_IFEXISTS) == 0 && (sp->se_flags & SE_IFCONSOLE) == 0) return (0); fd = open(sp->se_device, O_RDONLY | O_NONBLOCK, 0); if (fd < 0) { if (errno == ENOENT) return (1); return (0); } close(fd); return (0); } /* * Collect exit status for a child. * If an exiting login, start a new login running. */ static void collect_child(pid_t pid) { session_t *sp, *sprev, *snext; if (! sessions) return; if (! (sp = find_session(pid))) return; del_session(sp); sp->se_process = 0; if (sp->se_flags & SE_SHUTDOWN || session_has_no_tty(sp)) { if ((sprev = sp->se_prev) != NULL) sprev->se_next = sp->se_next; else sessions = sp->se_next; if ((snext = sp->se_next) != NULL) snext->se_prev = sp->se_prev; free_session(sp); return; } if ((pid = start_getty(sp)) == -1) { /* serious trouble */ requested_transition = clean_ttys; return; } sp->se_process = pid; sp->se_started = time((time_t *) 0); add_session(sp); } static const char * get_current_state(void) { if (current_state == single_user) return ("single-user"); if (current_state == runcom) return ("runcom"); if (current_state == read_ttys) return ("read-ttys"); if (current_state == multi_user) return ("multi-user"); if (current_state == clean_ttys) return ("clean-ttys"); if (current_state == catatonia) return ("catatonia"); if (current_state == death) return ("death"); if (current_state == death_single) return ("death-single"); return ("unknown"); } static void boottrace_transition(int sig) { const char *action; switch (sig) { case SIGUSR2: action = "halt & poweroff"; break; case SIGUSR1: action = "halt"; break; case SIGINT: action = "reboot"; break; case SIGWINCH: action = "powercycle"; break; case SIGTERM: action = Reboot ? "reboot" : "single-user"; break; default: BOOTTRACE("signal %d from %s", sig, get_current_state()); return; } /* Trace the shutdown reason. */ SHUTTRACE("%s from %s", action, get_current_state()); } /* * Catch a signal and request a state transition. */ static void transition_handler(int sig) { boottrace_transition(sig); switch (sig) { case SIGHUP: if (current_state == read_ttys || current_state == multi_user || current_state == clean_ttys || current_state == catatonia) requested_transition = clean_ttys; break; case SIGUSR2: howto = RB_POWEROFF; case SIGUSR1: howto |= RB_HALT; case SIGWINCH: case SIGINT: if (sig == SIGWINCH) howto |= RB_POWERCYCLE; Reboot = true; case SIGTERM: if (current_state == read_ttys || current_state == multi_user || current_state == clean_ttys || current_state == catatonia) requested_transition = death; else requested_transition = death_single; break; case SIGTSTP: if (current_state == runcom || current_state == read_ttys || current_state == clean_ttys || current_state == multi_user || current_state == catatonia) requested_transition = catatonia; break; case SIGEMT: requested_transition = reroot; break; default: requested_transition = 0; break; } } /* * Take the system multiuser. */ static state_func_t multi_user(void) { static bool inmultiuser = false; pid_t pid; session_t *sp; requested_transition = 0; /* * If the administrator has not set the security level to -1 * to indicate that the kernel should not run multiuser in secure * mode, and the run script has not set a higher level of security * than level 1, then put the kernel into secure mode. */ if (getsecuritylevel() == 0) setsecuritylevel(1); for (sp = sessions; sp; sp = sp->se_next) { if (sp->se_process) continue; if (session_has_no_tty(sp)) continue; if ((pid = start_getty(sp)) == -1) { /* serious trouble */ requested_transition = clean_ttys; break; } sp->se_process = pid; sp->se_started = time((time_t *) 0); add_session(sp); } if (requested_transition == 0 && !inmultiuser) { inmultiuser = true; /* This marks the change from boot-time tracing to run-time. */ RUNTRACE("multi-user start"); } while (!requested_transition) if ((pid = waitpid(-1, (int *) 0, 0)) != -1) collect_child(pid); return (state_func_t) requested_transition; } /* * This is an (n*2)+(n^2) algorithm. We hope it isn't run often... */ static state_func_t clean_ttys(void) { session_t *sp, *sprev; struct ttyent *typ; int devlen; char *old_getty, *old_window, *old_type; /* * mark all sessions for death, (!SE_PRESENT) * as we find or create new ones they'll be marked as keepers, * we'll later nuke all the ones not found in /etc/ttys */ for (sp = sessions; sp != NULL; sp = sp->se_next) sp->se_flags &= ~SE_PRESENT; devlen = sizeof(_PATH_DEV) - 1; while ((typ = getttyent()) != NULL) { for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next) if (strcmp(typ->ty_name, sp->se_device + devlen) == 0) break; if (sp) { /* we want this one to live */ sp->se_flags |= SE_PRESENT; if ((typ->ty_status & TTY_ON) == 0 || typ->ty_getty == 0) { sp->se_flags |= SE_SHUTDOWN; kill(sp->se_process, SIGHUP); continue; } sp->se_flags &= ~SE_SHUTDOWN; old_getty = sp->se_getty ? strdup(sp->se_getty) : 0; old_window = sp->se_window ? strdup(sp->se_window) : 0; old_type = sp->se_type ? strdup(sp->se_type) : 0; if (setupargv(sp, typ) == 0) { warning("can't parse getty for port %s", sp->se_device); sp->se_flags |= SE_SHUTDOWN; kill(sp->se_process, SIGHUP); } else if ( !old_getty || (!old_type && sp->se_type) || (old_type && !sp->se_type) || (!old_window && sp->se_window) || (old_window && !sp->se_window) || (strcmp(old_getty, sp->se_getty) != 0) || (old_window && strcmp(old_window, sp->se_window) != 0) || (old_type && strcmp(old_type, sp->se_type) != 0) ) { /* Don't set SE_SHUTDOWN here */ sp->se_nspace = 0; sp->se_started = 0; kill(sp->se_process, SIGHUP); } if (old_getty) free(old_getty); if (old_window) free(old_window); if (old_type) free(old_type); continue; } new_session(sprev, typ); } endttyent(); /* * sweep through and kill all deleted sessions * ones who's /etc/ttys line was deleted (SE_PRESENT unset) */ for (sp = sessions; sp != NULL; sp = sp->se_next) { if ((sp->se_flags & SE_PRESENT) == 0) { sp->se_flags |= SE_SHUTDOWN; kill(sp->se_process, SIGHUP); } } return (state_func_t) multi_user; } /* * Block further logins. */ static state_func_t catatonia(void) { session_t *sp; for (sp = sessions; sp; sp = sp->se_next) sp->se_flags |= SE_SHUTDOWN; return (state_func_t) multi_user; } /* * Note SIGALRM. */ static void alrm_handler(int sig) { (void)sig; clang = true; } /* * Bring the system down to single user. */ static state_func_t death(void) { int block, blocked; size_t len; /* Temporarily block suspend. */ len = sizeof(blocked); block = 1; if (sysctlbyname("kern.suspend_blocked", &blocked, &len, &block, sizeof(block)) == -1) blocked = 0; /* * Also revoke the TTY here. Because runshutdown() may reopen * the TTY whose getty we're killing here, there is no guarantee * runshutdown() will perform the initial open() call, causing * the terminal attributes to be misconfigured. */ revoke_ttys(); /* Try to run the rc.shutdown script within a period of time */ runshutdown(); /* Unblock suspend if we blocked it. */ if (!blocked) sysctlbyname("kern.suspend_blocked", NULL, NULL, &blocked, sizeof(blocked)); return (state_func_t) death_single; } /* * Do what is necessary to reinitialize single user mode or reboot * from an incomplete state. */ static state_func_t death_single(void) { int i; pid_t pid; static const int death_sigs[2] = { SIGTERM, SIGKILL }; revoke(_PATH_CONSOLE); BOOTTRACE("start killing user processes"); for (i = 0; i < 2; ++i) { if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) return (state_func_t) single_user; clang = false; alarm(DEATH_WATCH); do if ((pid = waitpid(-1, (int *)0, 0)) != -1) collect_child(pid); while (!clang && errno != ECHILD); if (errno == ECHILD) return (state_func_t) single_user; } warning("some processes would not die; ps axl advised"); return (state_func_t) single_user; } static void revoke_ttys(void) { session_t *sp; for (sp = sessions; sp; sp = sp->se_next) { sp->se_flags |= SE_SHUTDOWN; kill(sp->se_process, SIGHUP); revoke(sp->se_device); } } /* * Run the system shutdown script. * * Exit codes: XXX I should document more * -2 shutdown script terminated abnormally * -1 fatal error - can't run script * 0 good. * >0 some error (exit code) */ static int runshutdown(void) { pid_t pid, wpid; int status; int shutdowntimeout; size_t len; char *argv[SCRIPT_ARGV_SIZE]; struct stat sb; BOOTTRACE("init(8): start rc.shutdown"); /* * rc.shutdown is optional, so to prevent any unnecessary * complaints from the shell we simply don't run it if the * file does not exist. If the stat() here fails for other * reasons, we'll let the shell complain. */ if (stat(_PATH_RUNDOWN, &sb) == -1 && errno == ENOENT) return 0; if ((pid = fork()) == 0) { char _reboot[] = "reboot"; char _single[] = "single"; char _path_rundown[] = _PATH_RUNDOWN; argv[0] = _path_rundown; argv[1] = Reboot ? _reboot : _single; argv[2] = NULL; execute_script(argv); _exit(1); /* force single user mode */ } if (pid == -1) { emergency("can't fork for %s: %m", _PATH_RUNDOWN); while (waitpid(-1, (int *) 0, WNOHANG) > 0) continue; sleep(STALL_TIMEOUT); return -1; } len = sizeof(shutdowntimeout); if (sysctlbyname("kern.init_shutdown_timeout", &shutdowntimeout, &len, NULL, 0) == -1 || shutdowntimeout < 2) shutdowntimeout = DEATH_SCRIPT; alarm(shutdowntimeout); clang = false; /* * Copied from single_user(). This is a bit paranoid. * Use the same ALRM handler. */ do { if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) collect_child(wpid); if (clang) { /* we were waiting for the sub-shell */ kill(wpid, SIGTERM); warning("timeout expired for %s: %m; going to " "single user mode", _PATH_RUNDOWN); BOOTTRACE("rc.shutdown's %d sec timeout expired", shutdowntimeout); return -1; } if (wpid == -1) { if (errno == EINTR) continue; warning("wait for %s failed: %m; going to " "single user mode", _PATH_RUNDOWN); return -1; } if (wpid == pid && WIFSTOPPED(status)) { warning("init: %s stopped, restarting\n", _PATH_RUNDOWN); kill(pid, SIGCONT); wpid = -1; } } while (wpid != pid && !clang); /* Turn off the alarm */ alarm(0); if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && requested_transition == catatonia) { /* * /etc/rc.shutdown executed /sbin/reboot; * wait for the end quietly */ sigset_t s; sigfillset(&s); for (;;) sigsuspend(&s); } if (!WIFEXITED(status)) { warning("%s terminated abnormally, going to " "single user mode", _PATH_RUNDOWN); return -2; } if ((status = WEXITSTATUS(status)) != 0) warning("%s returned status %d", _PATH_RUNDOWN, status); return status; } static char * strk(char *p) { static char *t; char *q; int c; if (p) t = p; if (!t) return 0; c = *t; while (c == ' ' || c == '\t' ) c = *++t; if (!c) { t = 0; return 0; } q = t; if (c == '\'') { c = *++t; q = t; while (c && c != '\'') c = *++t; if (!c) /* unterminated string */ q = t = 0; else *t++ = 0; } else { while (c && c != ' ' && c != '\t' ) c = *++t; *t++ = 0; if (!c) t = 0; } return q; } #ifdef LOGIN_CAP static void setprocresources(const char *cname) { login_cap_t *lc; if ((lc = login_getclassbyname(cname, NULL)) != NULL) { setusercontext(lc, (struct passwd*)NULL, 0, LOGIN_SETENV | LOGIN_SETPRIORITY | LOGIN_SETRESOURCES | LOGIN_SETLOGINCLASS | LOGIN_SETCPUMASK); login_close(lc); } } #endif /* * Run /etc/rc.final to execute scripts after all user processes have been * terminated. */ static void runfinal(void) { struct stat sb; pid_t other_pid, pid; sigset_t mask; /* Avoid any surprises. */ alarm(0); /* rc.final is optional. */ if (stat(_PATH_RUNFINAL, &sb) == -1 && errno == ENOENT) return; if (access(_PATH_RUNFINAL, X_OK) != 0) { warning("%s exists, but not executable", _PATH_RUNFINAL); return; } pid = fork(); if (pid == 0) { /* * Reopen stdin/stdout/stderr so that scripts can write to * console. */ close(0); open(_PATH_DEVNULL, O_RDONLY); close(1); close(2); open_console(); dup2(1, 2); sigemptyset(&mask); sigprocmask(SIG_SETMASK, &mask, NULL); signal(SIGCHLD, SIG_DFL); execl(_PATH_RUNFINAL, _PATH_RUNFINAL, NULL); perror("execl(" _PATH_RUNFINAL ") failed"); exit(1); } /* Wait for rc.final script to exit */ while ((other_pid = waitpid(-1, NULL, 0)) != pid && other_pid > 0) { continue; } } diff --git a/sbin/mknod/mknod.c b/sbin/mknod/mknod.c index be7c97cf06fb..091c05cbfac2 100644 --- a/sbin/mknod/mknod.c +++ b/sbin/mknod/mknod.c @@ -1,169 +1,167 @@ /* * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Kevin Fall. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1989, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)mknod.c 8.1 (Berkeley) 6/5/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include static void usage(void) { (void)fprintf(stderr, "usage: mknod name\n" " mknod name [b | c] major minor [owner:group]\n"); exit(1); } static u_long id(const char *name, const char *type) { u_long val; char *ep; /* * XXX * We know that uid_t's and gid_t's are unsigned longs. */ errno = 0; val = strtoul(name, &ep, 10); if (errno) err(1, "%s", name); if (*ep != '\0') errx(1, "%s: illegal %s name", name, type); return (val); } static gid_t a_gid(const char *s) { struct group *gr; if (*s == '\0') /* Argument was "uid[:.]". */ errx(1, "group must be specified when the owner is"); return ((gr = getgrnam(s)) == NULL) ? id(s, "group") : gr->gr_gid; } static uid_t a_uid(const char *s) { struct passwd *pw; if (*s == '\0') /* Argument was "[:.]gid". */ errx(1, "owner must be specified when the group is"); return ((pw = getpwnam(s)) == NULL) ? id(s, "user") : pw->pw_uid; } int main(int argc, char **argv) { int range_error; uid_t uid; gid_t gid; mode_t mode; dev_t dev; char *cp, *endp; long mymajor, myminor; if (argc != 2 && argc != 5 && argc != 6) usage(); if (argc >= 5) { mode = 0666; if (argv[2][0] == 'c') mode |= S_IFCHR; else if (argv[2][0] == 'b') mode |= S_IFBLK; else errx(1, "node must be type 'b' or 'c'"); errno = 0; mymajor = (long)strtoul(argv[3], &endp, 0); if (endp == argv[3] || *endp != '\0') errx(1, "%s: non-numeric major number", argv[3]); range_error = errno; errno = 0; myminor = (long)strtoul(argv[4], &endp, 0); if (endp == argv[4] || *endp != '\0') errx(1, "%s: non-numeric minor number", argv[4]); range_error |= errno; dev = makedev(mymajor, myminor); if (range_error || major(dev) != mymajor || (long)(u_int)minor(dev) != myminor) errx(1, "major or minor number too large"); } else { mode = 0666 | S_IFCHR; dev = 0; } uid = gid = -1; if (6 == argc) { /* have owner:group */ if ((cp = strchr(argv[5], ':')) != NULL) { *cp++ = '\0'; gid = a_gid(cp); } else usage(); uid = a_uid(argv[5]); } if (mknod(argv[1], mode, dev) != 0) err(1, "%s", argv[1]); if (6 == argc) if (chown(argv[1], uid, gid)) err(1, "setting ownership on %s", argv[1]); exit(0); } diff --git a/sbin/mount/mount_fs.c b/sbin/mount/mount_fs.c index 5674e94594bf..7aac2b0ce104 100644 --- a/sbin/mount/mount_fs.c +++ b/sbin/mount/mount_fs.c @@ -1,141 +1,139 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1992, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software donated to Berkeley by * Jan-Simon Pendry. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1992, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)mount_fs.c 8.6 (Berkeley) 4/26/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include "extern.h" #include "mntopts.h" static struct mntopt mopts[] = { MOPT_STDOPTS, MOPT_END }; static void usage(void) { (void)fprintf(stderr, "usage: mount [-t fstype] [-o options] target_fs mount_point\n"); exit(1); } int mount_fs(const char *vfstype, int argc, char *argv[]) { struct iovec *iov; int iovlen; int mntflags = 0; int ch; char *dev, *dir, mntpath[MAXPATHLEN]; char fstype[32]; char errmsg[255]; char *p, *val; strlcpy(fstype, vfstype, sizeof(fstype)); memset(errmsg, 0, sizeof(errmsg)); getmnt_silent = 1; iov = NULL; iovlen = 0; optind = optreset = 1; /* Reset for parse of new argv. */ while ((ch = getopt(argc, argv, "o:")) != -1) { switch(ch) { case 'o': getmntopts(optarg, mopts, &mntflags, 0); p = strchr(optarg, '='); val = NULL; if (p != NULL) { *p = '\0'; val = p + 1; } build_iovec(&iov, &iovlen, optarg, val, (size_t)-1); break; case '?': default: usage(); } } argc -= optind; argv += optind; if (argc != 2) usage(); dev = argv[0]; dir = argv[1]; if (checkpath(dir, mntpath) != 0) { warn("%s", mntpath); return (1); } (void)rmslashes(dev, dev); build_iovec(&iov, &iovlen, "fstype", fstype, (size_t)-1); build_iovec(&iov, &iovlen, "fspath", mntpath, (size_t)-1); build_iovec(&iov, &iovlen, "from", dev, (size_t)-1); build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); if (nmount(iov, iovlen, mntflags) == -1) { if (*errmsg != '\0') warn("%s: %s", dev, errmsg); else warn("%s", dev); return (1); } return (0); } diff --git a/sbin/mount_cd9660/mount_cd9660.c b/sbin/mount_cd9660/mount_cd9660.c index f322ac73a439..f867e94da417 100644 --- a/sbin/mount_cd9660/mount_cd9660.c +++ b/sbin/mount_cd9660/mount_cd9660.c @@ -1,272 +1,270 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1992, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension * Support code is derived from software contributed to Berkeley * by Atsushi Murai (amurai@spec.co.jp). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)mount_cd9660.c 8.7 (Berkeley) 5/1/95 */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1992, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint /* static char sccsid[] = "@(#)mount_cd9660.c 8.7 (Berkeley) 5/1/95"; */ -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mntopts.h" static struct mntopt mopts[] = { MOPT_STDOPTS, MOPT_UPDATE, MOPT_END }; static int get_ssector(const char *dev); static int set_charset(struct iovec **, int *iovlen, const char *); void usage(void); int main(int argc, char **argv) { struct iovec *iov; int iovlen; int ch, mntflags; char *dev, *dir, *p, *val, mntpath[MAXPATHLEN]; int verbose; int ssector; /* starting sector, 0 for 1st session */ char fstype[] = "cd9660"; iov = NULL; iovlen = 0; mntflags = verbose = 0; ssector = -1; while ((ch = getopt(argc, argv, "begjo:rs:vC:")) != -1) switch (ch) { case 'b': build_iovec(&iov, &iovlen, "brokenjoliet", NULL, (size_t)-1); break; case 'e': build_iovec(&iov, &iovlen, "extatt", NULL, (size_t)-1); break; case 'g': build_iovec(&iov, &iovlen, "gens", NULL, (size_t)-1); break; case 'j': build_iovec(&iov, &iovlen, "nojoliet", NULL, (size_t)-1); break; case 'o': getmntopts(optarg, mopts, &mntflags, NULL); p = strchr(optarg, '='); val = NULL; if (p != NULL) { *p = '\0'; val = p + 1; } build_iovec(&iov, &iovlen, optarg, val, (size_t)-1); break; case 'r': build_iovec(&iov, &iovlen, "norrip", NULL, (size_t)-1); break; case 's': ssector = atoi(optarg); break; case 'v': verbose++; break; case 'C': if (set_charset(&iov, &iovlen, optarg) == -1) err(EX_OSERR, "cd9660_iconv"); build_iovec(&iov, &iovlen, "kiconv", NULL, (size_t)-1); break; case '?': default: usage(); } argc -= optind; argv += optind; if (argc != 2) usage(); dev = argv[0]; dir = argv[1]; /* * Resolve the mountpoint with realpath(3) and remove unnecessary * slashes from the devicename if there are any. */ if (checkpath(dir, mntpath) != 0) err(1, "%s", mntpath); (void)rmslashes(dev, dev); if (ssector == -1) { /* * The start of the session has not been specified on * the command line. If we can successfully read the * TOC of a CD-ROM, use the last data track we find. * Otherwise, just use 0, in order to mount the very * first session. This is compatible with the * historic behaviour of mount_cd9660(8). If the user * has specified -s above, we don't get here * and leave the user's will. */ if ((ssector = get_ssector(dev)) == -1) { if (verbose) printf("could not determine starting sector, " "using very first session\n"); ssector = 0; } else if (verbose) printf("using starting sector %d\n", ssector); } mntflags |= MNT_RDONLY; build_iovec(&iov, &iovlen, "fstype", fstype, (size_t)-1); build_iovec(&iov, &iovlen, "fspath", mntpath, (size_t)-1); build_iovec(&iov, &iovlen, "from", dev, (size_t)-1); build_iovec_argf(&iov, &iovlen, "ssector", "%d", ssector); if (nmount(iov, iovlen, mntflags) < 0) err(1, "%s", dev); exit(0); } void usage(void) { (void)fprintf(stderr, "usage: mount_cd9660 [-begjrv] [-C charset] [-o options] [-s startsector]\n" " special node\n"); exit(EX_USAGE); } static int get_ssector(const char *dev) { struct ioc_toc_header h; struct ioc_read_toc_entry t; struct cd_toc_entry toc_buffer[100]; int fd, ntocentries, i; if ((fd = open(dev, O_RDONLY)) == -1) return -1; if (ioctl(fd, CDIOREADTOCHEADER, &h) == -1) { close(fd); return -1; } ntocentries = h.ending_track - h.starting_track + 1; if (ntocentries > 100) { /* unreasonable, only 100 allowed */ close(fd); return -1; } t.address_format = CD_LBA_FORMAT; t.starting_track = 0; t.data_len = ntocentries * sizeof(struct cd_toc_entry); t.data = toc_buffer; if (ioctl(fd, CDIOREADTOCENTRYS, (char *) &t) == -1) { close(fd); return -1; } close(fd); for (i = ntocentries - 1; i >= 0; i--) if ((toc_buffer[i].control & 4) != 0) /* found a data track */ break; if (i < 0) return -1; return ntohl(toc_buffer[i].addr.lba); } static int set_charset(struct iovec **iov, int *iovlen, const char *localcs) { int error; char *cs_disk; /* disk charset for Joliet cs conversion */ char *cs_local; /* local charset for Joliet cs conversion */ cs_disk = NULL; cs_local = NULL; if (modfind("cd9660_iconv") < 0) if (kldload("cd9660_iconv") < 0 || modfind("cd9660_iconv") < 0) { warnx( "cannot find or load \"cd9660_iconv\" kernel module"); return (-1); } if ((cs_disk = malloc(ICONV_CSNMAXLEN)) == NULL) return (-1); if ((cs_local = malloc(ICONV_CSNMAXLEN)) == NULL) { free(cs_disk); return (-1); } strncpy(cs_disk, ENCODING_UNICODE, ICONV_CSNMAXLEN); strncpy(cs_local, kiconv_quirkcs(localcs, KICONV_VENDOR_MICSFT), ICONV_CSNMAXLEN); error = kiconv_add_xlat16_cspairs(cs_disk, cs_local); if (error) return (-1); build_iovec(iov, iovlen, "cs_disk", cs_disk, (size_t)-1); build_iovec(iov, iovlen, "cs_local", cs_local, (size_t)-1); return (0); } diff --git a/sbin/mount_msdosfs/mount_msdosfs.c b/sbin/mount_msdosfs/mount_msdosfs.c index 36be6a161ecf..9128914bc2b0 100644 --- a/sbin/mount_msdosfs/mount_msdosfs.c +++ b/sbin/mount_msdosfs/mount_msdosfs.c @@ -1,327 +1,322 @@ /* $NetBSD: mount_msdos.c,v 1.18 1997/09/16 12:24:18 lukem Exp $ */ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1994 Christopher G. Demetriou * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Christopher G. Demetriou. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include /* must be after stdio to declare fparseln */ #include #include #include #include #include #include "mntopts.h" static gid_t a_gid(char *); static uid_t a_uid(char *); static mode_t a_mask(char *); static void usage(void) __dead2; static int set_charset(struct iovec **iov, int *iovlen, const char *, const char *); int main(int argc, char **argv) { struct iovec *iov = NULL; int iovlen = 0; struct stat sb; int c, set_gid, set_uid, set_mask, set_dirmask; char *dev, *dir, mntpath[MAXPATHLEN], *csp; char fstype[] = "msdosfs"; char errmsg[255] = {0}; char *cs_dos = NULL; char *cs_local = NULL; mode_t mask = 0, dirmask = 0; uid_t uid = 0; gid_t gid = 0; set_gid = set_uid = set_mask = set_dirmask = 0; while ((c = getopt(argc, argv, "sl9u:g:m:M:o:L:D:W:")) != -1) { switch (c) { case 's': build_iovec(&iov, &iovlen, "shortnames", NULL, (size_t)-1); break; case 'l': build_iovec(&iov, &iovlen, "longnames", NULL, (size_t)-1); break; case '9': build_iovec_argf(&iov, &iovlen, "nowin95", "", (size_t)-1); break; case 'u': uid = a_uid(optarg); set_uid = 1; break; case 'g': gid = a_gid(optarg); set_gid = 1; break; case 'm': mask = a_mask(optarg); set_mask = 1; break; case 'M': dirmask = a_mask(optarg); set_dirmask = 1; break; case 'L': { const char *quirk = NULL; if (setlocale(LC_CTYPE, optarg) == NULL) err(EX_CONFIG, "%s", optarg); csp = strchr(optarg,'.'); if (!csp) err(EX_CONFIG, "%s", optarg); quirk = kiconv_quirkcs(csp + 1, KICONV_VENDOR_MICSFT); build_iovec_argf(&iov, &iovlen, "cs_local", quirk); cs_local = strdup(quirk); } break; case 'D': cs_dos = strdup(optarg); build_iovec_argf(&iov, &iovlen, "cs_dos", cs_dos, (size_t)-1); break; case 'o': { char *p = NULL; char *val = strdup(""); p = strchr(optarg, '='); if (p != NULL) { free(val); *p = '\0'; val = p + 1; } build_iovec(&iov, &iovlen, optarg, val, (size_t)-1); } break; case 'W': if (strcmp(optarg, "iso22dos") == 0) { cs_local = strdup("ISO8859-2"); cs_dos = strdup("CP852"); } else if (strcmp(optarg, "iso72dos") == 0) { cs_local = strdup("ISO8859-7"); cs_dos = strdup("CP737"); } else if (strcmp(optarg, "koi2dos") == 0) { cs_local = strdup("KOI8-R"); cs_dos = strdup("CP866"); } else if (strcmp(optarg, "koi8u2dos") == 0) { cs_local = strdup("KOI8-U"); cs_dos = strdup("CP866"); } else { err(EX_NOINPUT, "%s", optarg); } build_iovec(&iov, &iovlen, "cs_local", cs_local, (size_t)-1); build_iovec(&iov, &iovlen, "cs_dos", cs_dos, (size_t)-1); break; case '?': default: usage(); break; } } if (optind + 2 != argc) usage(); if (set_mask && !set_dirmask) { dirmask = mask; set_dirmask = 1; } else if (set_dirmask && !set_mask) { mask = dirmask; set_mask = 1; } dev = argv[optind]; dir = argv[optind + 1]; if (cs_local != NULL) { if (set_charset(&iov, &iovlen, cs_local, cs_dos) == -1) err(EX_OSERR, "msdosfs_iconv"); build_iovec_argf(&iov, &iovlen, "kiconv", ""); } else if (cs_dos != NULL) { build_iovec_argf(&iov, &iovlen, "cs_local", "ISO8859-1"); if (set_charset(&iov, &iovlen, "ISO8859-1", cs_dos) == -1) err(EX_OSERR, "msdosfs_iconv"); build_iovec_argf(&iov, &iovlen, "kiconv", ""); } /* * Resolve the mountpoint with realpath(3) and remove unnecessary * slashes from the devicename if there are any. */ if (checkpath(dir, mntpath) != 0) err(EX_USAGE, "%s", mntpath); (void)rmslashes(dev, dev); if (!set_gid || !set_uid || !set_mask) { if (stat(mntpath, &sb) == -1) err(EX_OSERR, "stat %s", mntpath); if (!set_uid) uid = sb.st_uid; if (!set_gid) gid = sb.st_gid; if (!set_mask) mask = dirmask = sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); } build_iovec(&iov, &iovlen, "fstype", fstype, (size_t)-1); build_iovec(&iov, &iovlen, "fspath", mntpath, (size_t)-1); build_iovec(&iov, &iovlen, "from", dev, (size_t)-1); build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); build_iovec_argf(&iov, &iovlen, "uid", "%d", uid); build_iovec_argf(&iov, &iovlen, "gid", "%u", gid); build_iovec_argf(&iov, &iovlen, "mask", "%u", mask); build_iovec_argf(&iov, &iovlen, "dirmask", "%u", dirmask); if (nmount(iov, iovlen, 0) < 0) { if (errmsg[0]) err(1, "%s: %s", dev, errmsg); else err(1, "%s", dev); } exit (0); } gid_t a_gid(char *s) { struct group *gr; char *gname; gid_t gid; if ((gr = getgrnam(s)) != NULL) gid = gr->gr_gid; else { for (gname = s; *s && isdigit(*s); ++s); if (!*s) gid = atoi(gname); else errx(EX_NOUSER, "unknown group id: %s", gname); } return (gid); } uid_t a_uid(char *s) { struct passwd *pw; char *uname; uid_t uid; if ((pw = getpwnam(s)) != NULL) uid = pw->pw_uid; else { for (uname = s; *s && isdigit(*s); ++s); if (!*s) uid = atoi(uname); else errx(EX_NOUSER, "unknown user id: %s", uname); } return (uid); } mode_t a_mask(char *s) { int done, rv; char *ep; done = 0; rv = -1; if (*s >= '0' && *s <= '7') { done = 1; rv = strtol(optarg, &ep, 8); } if (!done || rv < 0 || *ep) errx(EX_USAGE, "invalid file mode: %s", s); return (rv); } void usage(void) { fprintf(stderr, "%s\n%s\n%s\n", "usage: mount_msdosfs [-9ls] [-D DOS_codepage] [-g gid] [-L locale]", " [-M mask] [-m mask] [-o options] [-u uid]", " [-W table] special node"); exit(EX_USAGE); } int set_charset(struct iovec **iov, int *iovlen, const char *cs_local, const char *cs_dos) { int error; if (modfind("msdosfs_iconv") < 0) if (kldload("msdosfs_iconv") < 0 || modfind("msdosfs_iconv") < 0) { warnx("cannot find or load \"msdosfs_iconv\" kernel module"); return (-1); } build_iovec_argf(iov, iovlen, "cs_win", ENCODING_UNICODE); error = kiconv_add_xlat16_cspairs(ENCODING_UNICODE, cs_local); if (error && errno != EEXIST) return (-1); if (cs_dos != NULL) { error = kiconv_add_xlat16_cspairs(cs_dos, cs_local); if (error && errno != EEXIST) return (-1); } else { build_iovec_argf(iov, iovlen, "cs_dos", cs_local); error = kiconv_add_xlat16_cspair(cs_local, cs_local, KICONV_FROM_UPPER | KICONV_LOWER); if (error && errno != EEXIST) return (-1); } return (0); } diff --git a/sbin/mount_nullfs/mount_nullfs.c b/sbin/mount_nullfs/mount_nullfs.c index 55d7ac982f70..c6bf09a1ca1c 100644 --- a/sbin/mount_nullfs/mount_nullfs.c +++ b/sbin/mount_nullfs/mount_nullfs.c @@ -1,144 +1,142 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1992, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software donated to Berkeley by * Jan-Simon Pendry. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1992, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)mount_null.c 8.6 (Berkeley) 4/26/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include "mntopts.h" static void usage(void) __dead2; static int stat_realpath(const char *path, char *resolved, struct stat *sbp) { if (realpath(path, resolved) == NULL || stat(resolved, sbp) != 0) return (1); return (0); } int main(int argc, char *argv[]) { struct iovec *iov; char *p, *val; char mountpoint[MAXPATHLEN]; char target[MAXPATHLEN]; char errmsg[255]; int ch, iovlen; char nullfs[] = "nullfs"; struct stat target_stat; struct stat mountpoint_stat; iov = NULL; iovlen = 0; errmsg[0] = '\0'; while ((ch = getopt(argc, argv, "o:")) != -1) switch(ch) { case 'o': val = strdup(""); p = strchr(optarg, '='); if (p != NULL) { free(val); *p = '\0'; val = p + 1; } build_iovec(&iov, &iovlen, optarg, val, (size_t)-1); break; case '?': default: usage(); } argc -= optind; argv += optind; if (argc != 2) usage(); /* resolve target and mountpoint with realpath(3) */ if (stat_realpath(argv[0], target, &target_stat) != 0) err(EX_USAGE, "%s", target); if (stat_realpath(argv[1], mountpoint, &mountpoint_stat) != 0) err(EX_USAGE, "%s", mountpoint); if (!S_ISDIR(target_stat.st_mode) && !S_ISREG(target_stat.st_mode)) errx(EX_USAGE, "%s: must be either a file or directory", target); if ((target_stat.st_mode & S_IFMT) != (mountpoint_stat.st_mode & S_IFMT)) errx(EX_USAGE, "%s: must be same type as %s (file or directory)", mountpoint, target); build_iovec(&iov, &iovlen, "fstype", nullfs, (size_t)-1); build_iovec(&iov, &iovlen, "fspath", mountpoint, (size_t)-1); build_iovec(&iov, &iovlen, "target", target, (size_t)-1); build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); if (nmount(iov, iovlen, 0) < 0) { if (errmsg[0] != 0) err(1, "%s: %s", mountpoint, errmsg); else err(1, "%s", mountpoint); } exit(0); } static void usage(void) { (void)fprintf(stderr, "usage: mount_nullfs [-o options] target mount-point\n"); exit(1); } diff --git a/sbin/mount_unionfs/mount_unionfs.c b/sbin/mount_unionfs/mount_unionfs.c index 9aafaf13d81f..01c32f267923 100644 --- a/sbin/mount_unionfs/mount_unionfs.c +++ b/sbin/mount_unionfs/mount_unionfs.c @@ -1,197 +1,194 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1992, 1993, 1994 * The Regents of the University of California. * Copyright (c) 2005, 2006 Masanori Ozawa , ONGS Inc. * Copyright (c) 2006 Daichi Goto * All rights reserved. * * This code is derived from software donated to Berkeley by * Jan-Simon Pendry. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1992, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)mount_union.c 8.5 (Berkeley) 3/27/94"; -#else -static const char rcsid[] = - "$FreeBSD$"; #endif #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include "mntopts.h" static int subdir(const char *p, const char *dir) { int l; l = strlen(dir); if (l <= 1) return (1); if ((strncmp(p, dir, l) == 0) && (p[l] == '/' || p[l] == '\0')) return (1); return (0); } static void usage(void) { (void)fprintf(stderr, "usage: mount_unionfs [-o options] directory uniondir\n"); exit(EX_USAGE); } static void parse_gid(const char *s, char *buf, size_t bufsize) { struct group *gr; char *inval; if ((gr = getgrnam(s)) != NULL) snprintf(buf, bufsize, "%d", gr->gr_gid); else { strtol(s, &inval, 10); if (*inval != 0) { errx(EX_NOUSER, "unknown group id: %s", s); usage(); } else { strncpy(buf, s, bufsize); } } } static void parse_uid(const char *s, char *buf, size_t bufsize) { struct passwd *pw; char *inval; if ((pw = getpwnam(s)) != NULL) snprintf(buf, bufsize, "%d", pw->pw_uid); else { strtol(s, &inval, 10); if (*inval != 0) { errx(EX_NOUSER, "unknown user id: %s", s); usage(); } else { strncpy(buf, s, bufsize); } } } int main(int argc, char *argv[]) { struct iovec *iov; int ch, iovlen; char source [MAXPATHLEN], target[MAXPATHLEN], errmsg[255]; char uid_str[20], gid_str[20]; char fstype[] = "unionfs"; char *p, *val; iov = NULL; iovlen = 0; memset(errmsg, 0, sizeof(errmsg)); while ((ch = getopt(argc, argv, "bo:")) != -1) { switch (ch) { case 'b': printf("\n -b is deprecated. Use \"-o below\" instead\n"); build_iovec(&iov, &iovlen, "below", NULL, 0); break; case 'o': p = strchr(optarg, '='); val = NULL; if (p != NULL) { *p = '\0'; val = p + 1; if (strcmp(optarg, "gid") == 0) { parse_gid(val, gid_str, sizeof(gid_str)); val = gid_str; } else if (strcmp(optarg, "uid") == 0) { parse_uid(val, uid_str, sizeof(uid_str)); val = uid_str; } } build_iovec(&iov, &iovlen, optarg, val, (size_t)-1); break; case '?': default: usage(); /* NOTREACHED */ } } argc -= optind; argv += optind; if (argc != 2) usage(); /* resolve both target and source with realpath(3) */ if (checkpath(argv[0], target) != 0) err(EX_USAGE, "%s", target); if (checkpath(argv[1], source) != 0) err(EX_USAGE, "%s", source); if (subdir(target, source) || subdir(source, target)) errx(EX_USAGE, "%s (%s) and %s (%s) are not distinct paths", argv[0], target, argv[1], source); build_iovec(&iov, &iovlen, "fstype", fstype, (size_t)-1); build_iovec(&iov, &iovlen, "fspath", source, (size_t)-1); build_iovec(&iov, &iovlen, "from", target, (size_t)-1); build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); if (nmount(iov, iovlen, 0)) err(EX_OSERR, "%s: %s", source, errmsg); exit(0); } diff --git a/sbin/newfs_msdos/mkfs_msdos.c b/sbin/newfs_msdos/mkfs_msdos.c index 13a804c82625..065e3c5f4192 100644 --- a/sbin/newfs_msdos/mkfs_msdos.c +++ b/sbin/newfs_msdos/mkfs_msdos.c @@ -1,1103 +1,1098 @@ /* * Copyright (c) 1998 Robert Nordier * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #ifdef MAKEFS /* In the makefs case we only want struct disklabel */ #include #else #include #include #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mkfs_msdos.h" #define MAXU16 0xffff /* maximum unsigned 16-bit quantity */ #define BPN 4 /* bits per nibble */ #define NPB 2 /* nibbles per byte */ #define DOSMAGIC 0xaa55 /* DOS magic number */ #define MINBPS 512 /* minimum bytes per sector */ #define MAXBPS 4096 /* maximum bytes per sector */ #define MAXSPC 128 /* maximum sectors per cluster */ #define MAXNFT 16 /* maximum number of FATs */ #define DEFBLK 4096 /* default block size */ #define DEFBLK16 2048 /* default block size FAT16 */ #define DEFRDE 512 /* default root directory entries */ #define RESFTE 2 /* reserved FAT entries */ #define MINCLS12 1U /* minimum FAT12 clusters */ #define MINCLS16 0xff5U /* minimum FAT16 clusters */ #define MINCLS32 0xfff5U /* minimum FAT32 clusters */ #define MAXCLS12 0xff4U /* maximum FAT12 clusters */ #define MAXCLS16 0xfff4U /* maximum FAT16 clusters */ #define MAXCLS32 0xffffff4U /* maximum FAT32 clusters */ #define mincls(fat) ((fat) == 12 ? MINCLS12 : \ (fat) == 16 ? MINCLS16 : \ MINCLS32) #define maxcls(fat) ((fat) == 12 ? MAXCLS12 : \ (fat) == 16 ? MAXCLS16 : \ MAXCLS32) #define mk1(p, x) \ (p) = (u_int8_t)(x) #define mk2(p, x) \ (p)[0] = (u_int8_t)(x), \ (p)[1] = (u_int8_t)((x) >> 010) #define mk4(p, x) \ (p)[0] = (u_int8_t)(x), \ (p)[1] = (u_int8_t)((x) >> 010), \ (p)[2] = (u_int8_t)((x) >> 020), \ (p)[3] = (u_int8_t)((x) >> 030) struct bs { u_int8_t bsJump[3]; /* bootstrap entry point */ u_int8_t bsOemName[8]; /* OEM name and version */ } __packed; struct bsbpb { u_int8_t bpbBytesPerSec[2]; /* bytes per sector */ u_int8_t bpbSecPerClust; /* sectors per cluster */ u_int8_t bpbResSectors[2]; /* reserved sectors */ u_int8_t bpbFATs; /* number of FATs */ u_int8_t bpbRootDirEnts[2]; /* root directory entries */ u_int8_t bpbSectors[2]; /* total sectors */ u_int8_t bpbMedia; /* media descriptor */ u_int8_t bpbFATsecs[2]; /* sectors per FAT */ u_int8_t bpbSecPerTrack[2]; /* sectors per track */ u_int8_t bpbHeads[2]; /* drive heads */ u_int8_t bpbHiddenSecs[4]; /* hidden sectors */ u_int8_t bpbHugeSectors[4]; /* big total sectors */ } __packed; struct bsxbpb { u_int8_t bpbBigFATsecs[4]; /* big sectors per FAT */ u_int8_t bpbExtFlags[2]; /* FAT control flags */ u_int8_t bpbFSVers[2]; /* file system version */ u_int8_t bpbRootClust[4]; /* root directory start cluster */ u_int8_t bpbFSInfo[2]; /* file system info sector */ u_int8_t bpbBackup[2]; /* backup boot sector */ u_int8_t bpbReserved[12]; /* reserved */ } __packed; struct bsx { u_int8_t exDriveNumber; /* drive number */ u_int8_t exReserved1; /* reserved */ u_int8_t exBootSignature; /* extended boot signature */ u_int8_t exVolumeID[4]; /* volume ID number */ u_int8_t exVolumeLabel[11]; /* volume label */ u_int8_t exFileSysType[8]; /* file system type */ } __packed; struct de { u_int8_t deName[11]; /* name and extension */ u_int8_t deAttributes; /* attributes */ u_int8_t rsvd[10]; /* reserved */ u_int8_t deMTime[2]; /* last-modified time */ u_int8_t deMDate[2]; /* last-modified date */ u_int8_t deStartCluster[2]; /* starting cluster */ u_int8_t deFileSize[4]; /* size */ } __packed; struct bpb { u_int bpbBytesPerSec; /* bytes per sector */ u_int bpbSecPerClust; /* sectors per cluster */ u_int bpbResSectors; /* reserved sectors */ u_int bpbFATs; /* number of FATs */ u_int bpbRootDirEnts; /* root directory entries */ u_int bpbSectors; /* total sectors */ u_int bpbMedia; /* media descriptor */ u_int bpbFATsecs; /* sectors per FAT */ u_int bpbSecPerTrack; /* sectors per track */ u_int bpbHeads; /* drive heads */ u_int bpbHiddenSecs; /* hidden sectors */ u_int bpbHugeSectors; /* big total sectors */ u_int bpbBigFATsecs; /* big sectors per FAT */ u_int bpbRootClust; /* root directory start cluster */ u_int bpbFSInfo; /* file system info sector */ u_int bpbBackup; /* backup boot sector */ }; #define BPBGAP 0, 0, 0, 0, 0, 0 static struct { const char *name; struct bpb bpb; } const stdfmt[] = { {"160", {512, 1, 1, 2, 64, 320, 0xfe, 1, 8, 1, BPBGAP}}, {"180", {512, 1, 1, 2, 64, 360, 0xfc, 2, 9, 1, BPBGAP}}, {"320", {512, 2, 1, 2, 112, 640, 0xff, 1, 8, 2, BPBGAP}}, {"360", {512, 2, 1, 2, 112, 720, 0xfd, 2, 9, 2, BPBGAP}}, {"640", {512, 2, 1, 2, 112, 1280, 0xfb, 2, 8, 2, BPBGAP}}, {"720", {512, 2, 1, 2, 112, 1440, 0xf9, 3, 9, 2, BPBGAP}}, {"1200", {512, 1, 1, 2, 224, 2400, 0xf9, 7, 15, 2, BPBGAP}}, {"1232", {1024,1, 1, 2, 192, 1232, 0xfe, 2, 8, 2, BPBGAP}}, {"1440", {512, 1, 1, 2, 224, 2880, 0xf0, 9, 18, 2, BPBGAP}}, {"2880", {512, 2, 1, 2, 240, 5760, 0xf0, 9, 36, 2, BPBGAP}} }; static const u_int8_t bootcode[] = { 0xfa, /* cli */ 0x31, 0xc0, /* xor ax,ax */ 0x8e, 0xd0, /* mov ss,ax */ 0xbc, 0x00, 0x7c, /* mov sp,7c00h */ 0xfb, /* sti */ 0x8e, 0xd8, /* mov ds,ax */ 0xe8, 0x00, 0x00, /* call $ + 3 */ 0x5e, /* pop si */ 0x83, 0xc6, 0x19, /* add si,+19h */ 0xbb, 0x07, 0x00, /* mov bx,0007h */ 0xfc, /* cld */ 0xac, /* lodsb */ 0x84, 0xc0, /* test al,al */ 0x74, 0x06, /* jz $ + 8 */ 0xb4, 0x0e, /* mov ah,0eh */ 0xcd, 0x10, /* int 10h */ 0xeb, 0xf5, /* jmp $ - 9 */ 0x30, 0xe4, /* xor ah,ah */ 0xcd, 0x16, /* int 16h */ 0xcd, 0x19, /* int 19h */ 0x0d, 0x0a, 'N', 'o', 'n', '-', 's', 'y', 's', 't', 'e', 'm', ' ', 'd', 'i', 's', 'k', 0x0d, 0x0a, 'P', 'r', 'e', 's', 's', ' ', 'a', 'n', 'y', ' ', 'k', 'e', 'y', ' ', 't', 'o', ' ', 'r', 'e', 'b', 'o', 'o', 't', 0x0d, 0x0a, 0 }; static volatile sig_atomic_t got_siginfo; static void infohandler(int); #ifndef MAKEFS static int check_mounted(const char *, mode_t); #endif static ssize_t getchunksize(void); static int getstdfmt(const char *, struct bpb *); static int getdiskinfo(int, const char *, const char *, int, struct bpb *); static void print_bpb(struct bpb *); static int ckgeom(const char *, u_int, const char *); static void mklabel(u_int8_t *, const char *); static int oklabel(const char *); static void setstr(u_int8_t *, const char *, size_t); int mkfs_msdos(const char *fname, const char *dtype, const struct msdos_options *op) { char buf[MAXPATHLEN]; struct sigaction si_sa; struct stat sb; struct timeval tv; struct bpb bpb; struct tm *tm; struct bs *bs; struct bsbpb *bsbpb; struct bsxbpb *bsxbpb; struct bsx *bsx; struct de *de; u_int8_t *img; u_int8_t *physbuf, *physbuf_end; const char *bname; ssize_t n; time_t now; u_int fat, bss, rds, cls, dir, lsn, x, x1, x2; u_int extra_res, alignment, saved_x, attempts=0; bool set_res, set_spf, set_spc; int fd, fd1, rv; struct msdos_options o = *op; ssize_t chunksize; physbuf = NULL; rv = -1; fd = fd1 = -1; if (o.block_size && o.sectors_per_cluster) { warnx("Cannot specify both block size and sectors per cluster"); goto done; } if (o.OEM_string && strlen(o.OEM_string) > 8) { warnx("%s: bad OEM string", o.OEM_string); goto done; } if (o.create_size) { if (o.no_create) { warnx("create (-C) is incompatible with -N"); goto done; } fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644); if (fd == -1) { warnx("failed to create %s", fname); goto done; } if (ftruncate(fd, o.create_size)) { warnx("failed to initialize %jd bytes", (intmax_t)o.create_size); goto done; } } else if ((fd = open(fname, o.no_create ? O_RDONLY : O_RDWR)) == -1) { warn("%s", fname); goto done; } if (fstat(fd, &sb)) { warn("%s", fname); goto done; } if (o.create_size) { if (!S_ISREG(sb.st_mode)) warnx("warning, %s is not a regular file", fname); } else { #ifdef MAKEFS errx(1, "o.create_size must be set!"); #else if (!S_ISCHR(sb.st_mode)) warnx("warning, %s is not a character device", fname); #endif } #ifndef MAKEFS if (!o.no_create) if (check_mounted(fname, sb.st_mode) == -1) goto done; #endif if (o.offset && o.offset != lseek(fd, o.offset, SEEK_SET)) { warnx("cannot seek to %jd", (intmax_t)o.offset); goto done; } memset(&bpb, 0, sizeof(bpb)); if (o.floppy) { if (getstdfmt(o.floppy, &bpb) == -1) goto done; bpb.bpbHugeSectors = bpb.bpbSectors; bpb.bpbSectors = 0; bpb.bpbBigFATsecs = bpb.bpbFATsecs; bpb.bpbFATsecs = 0; } if (o.drive_heads) bpb.bpbHeads = o.drive_heads; if (o.sectors_per_track) bpb.bpbSecPerTrack = o.sectors_per_track; if (o.bytes_per_sector) bpb.bpbBytesPerSec = o.bytes_per_sector; if (o.size) bpb.bpbHugeSectors = o.size; if (o.hidden_sectors_set) bpb.bpbHiddenSecs = o.hidden_sectors; if (!(o.floppy || (o.drive_heads && o.sectors_per_track && o.bytes_per_sector && o.size && o.hidden_sectors_set))) { if (getdiskinfo(fd, fname, dtype, o.hidden_sectors_set, &bpb) == -1) goto done; bpb.bpbHugeSectors -= (o.offset / bpb.bpbBytesPerSec); if (bpb.bpbSecPerClust == 0) { /* set defaults */ if (bpb.bpbHugeSectors <= 6000) /* about 3MB -> 512 bytes */ bpb.bpbSecPerClust = 1; else if (bpb.bpbHugeSectors <= (1<<17)) /* 64M -> 4k */ bpb.bpbSecPerClust = 8; else if (bpb.bpbHugeSectors <= (1<<19)) /* 256M -> 8k */ bpb.bpbSecPerClust = 16; else if (bpb.bpbHugeSectors <= (1<<21)) /* 1G -> 16k */ bpb.bpbSecPerClust = 32; else bpb.bpbSecPerClust = 64; /* otherwise 32k */ } } if (bpb.bpbBytesPerSec < MINBPS || bpb.bpbBytesPerSec > MAXBPS || !powerof2(bpb.bpbBytesPerSec)) { warnx("Invalid bytes/sector (%u): must be 512, 1024, 2048 or 4096", bpb.bpbBytesPerSec); goto done; } if (o.volume_label && !oklabel(o.volume_label)) { warnx("%s: bad volume label", o.volume_label); goto done; } if (!(fat = o.fat_type)) { if (o.floppy) fat = 12; else if (!o.directory_entries && (o.info_sector || o.backup_sector)) fat = 32; } if ((fat == 32 && o.directory_entries) || (fat != 32 && (o.info_sector || o.backup_sector))) { warnx("-%c is not a legal FAT%s option", fat == 32 ? 'e' : o.info_sector ? 'i' : 'k', fat == 32 ? "32" : "12/16"); goto done; } if (o.floppy && fat == 32) bpb.bpbRootDirEnts = 0; if (fat != 0 && fat != 12 && fat != 16 && fat != 32) { warnx("%d: bad FAT type", fat); goto done; } if (o.block_size) { if (!powerof2(o.block_size)) { warnx("block size (%u) is not a power of 2", o.block_size); goto done; } if (o.block_size < bpb.bpbBytesPerSec) { warnx("block size (%u) is too small; minimum is %u", o.block_size, bpb.bpbBytesPerSec); goto done; } if (o.block_size > bpb.bpbBytesPerSec * MAXSPC) { warnx("block size (%u) is too large; maximum is %u", o.block_size, bpb.bpbBytesPerSec * MAXSPC); goto done; } bpb.bpbSecPerClust = o.block_size / bpb.bpbBytesPerSec; } if (o.sectors_per_cluster) { if (!powerof2(o.sectors_per_cluster)) { warnx("sectors/cluster (%u) is not a power of 2", o.sectors_per_cluster); goto done; } bpb.bpbSecPerClust = o.sectors_per_cluster; } if (o.reserved_sectors) bpb.bpbResSectors = o.reserved_sectors; if (o.num_FAT) { if (o.num_FAT > MAXNFT) { warnx("number of FATs (%u) is too large; maximum is %u", o.num_FAT, MAXNFT); goto done; } bpb.bpbFATs = o.num_FAT; } if (o.directory_entries) bpb.bpbRootDirEnts = o.directory_entries; if (o.media_descriptor_set) { if (o.media_descriptor < 0xf0) { warnx("illegal media descriptor (%#x)", o.media_descriptor); goto done; } bpb.bpbMedia = o.media_descriptor; } if (o.sectors_per_fat) bpb.bpbBigFATsecs = o.sectors_per_fat; if (o.info_sector) bpb.bpbFSInfo = o.info_sector; if (o.backup_sector) bpb.bpbBackup = o.backup_sector; bss = 1; bname = NULL; fd1 = -1; if (o.bootstrap) { bname = o.bootstrap; if (!strchr(bname, '/')) { snprintf(buf, sizeof(buf), "/boot/%s", bname); bname = buf; } if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb)) { warn("%s", bname); goto done; } if (!S_ISREG(sb.st_mode) || sb.st_size % bpb.bpbBytesPerSec || sb.st_size < bpb.bpbBytesPerSec || sb.st_size > bpb.bpbBytesPerSec * MAXU16) { warnx("%s: inappropriate file type or format", bname); goto done; } bss = sb.st_size / bpb.bpbBytesPerSec; } if (!bpb.bpbFATs) bpb.bpbFATs = 2; if (!fat) { if (bpb.bpbHugeSectors < (bpb.bpbResSectors ? bpb.bpbResSectors : bss) + howmany((RESFTE + (bpb.bpbSecPerClust ? MINCLS16 : MAXCLS12 + 1)) * (bpb.bpbSecPerClust ? 16 : 12) / BPN, bpb.bpbBytesPerSec * NPB) * bpb.bpbFATs + howmany(bpb.bpbRootDirEnts ? bpb.bpbRootDirEnts : DEFRDE, bpb.bpbBytesPerSec / sizeof(struct de)) + (bpb.bpbSecPerClust ? MINCLS16 : MAXCLS12 + 1) * (bpb.bpbSecPerClust ? bpb.bpbSecPerClust : howmany(DEFBLK, bpb.bpbBytesPerSec))) fat = 12; else if (bpb.bpbRootDirEnts || bpb.bpbHugeSectors < (bpb.bpbResSectors ? bpb.bpbResSectors : bss) + howmany((RESFTE + MAXCLS16) * 2, bpb.bpbBytesPerSec) * bpb.bpbFATs + howmany(DEFRDE, bpb.bpbBytesPerSec / sizeof(struct de)) + (MAXCLS16 + 1) * (bpb.bpbSecPerClust ? bpb.bpbSecPerClust : howmany(8192, bpb.bpbBytesPerSec))) fat = 16; else fat = 32; } x = bss; if (fat == 32) { if (!bpb.bpbFSInfo) { if (x == MAXU16 || x == bpb.bpbBackup) { warnx("no room for info sector"); goto done; } bpb.bpbFSInfo = x; } if (bpb.bpbFSInfo != MAXU16 && x <= bpb.bpbFSInfo) x = bpb.bpbFSInfo + 1; if (!bpb.bpbBackup) { if (x == MAXU16) { warnx("no room for backup sector"); goto done; } bpb.bpbBackup = x; } else if (bpb.bpbBackup != MAXU16 && bpb.bpbBackup == bpb.bpbFSInfo) { warnx("backup sector would overwrite info sector"); goto done; } if (bpb.bpbBackup != MAXU16 && x <= bpb.bpbBackup) x = bpb.bpbBackup + 1; } extra_res = 0; alignment = 0; set_res = (bpb.bpbResSectors == 0); set_spf = (bpb.bpbBigFATsecs == 0); set_spc = (bpb.bpbSecPerClust == 0); saved_x = x; /* * Attempt to align the root directory to cluster if o.align is set. * This is done by padding with reserved blocks. Note that this can * cause other factors to change, which can in turn change the alignment. * This should take at most 2 iterations, as increasing the reserved * amount may cause the FAT size to decrease by 1, requiring another * bpbFATs reserved blocks. If bpbSecPerClust changes, it will * be half of its previous size, and thus will not throw off alignment. */ do { x = saved_x; if (set_res) bpb.bpbResSectors = ((fat == 32) ? MAX(x, MAX(16384 / bpb.bpbBytesPerSec, 4)) : x) + extra_res; else if (bpb.bpbResSectors < x) { warnx("too few reserved sectors (need %d have %d)", x, bpb.bpbResSectors); goto done; } if (fat != 32 && !bpb.bpbRootDirEnts) bpb.bpbRootDirEnts = DEFRDE; rds = howmany(bpb.bpbRootDirEnts, bpb.bpbBytesPerSec / sizeof(struct de)); if (set_spc) { for (bpb.bpbSecPerClust = howmany(fat == 16 ? DEFBLK16 : DEFBLK, bpb.bpbBytesPerSec); bpb.bpbSecPerClust < MAXSPC && (bpb.bpbResSectors + howmany((RESFTE + maxcls(fat)) * (fat / BPN), bpb.bpbBytesPerSec * NPB) * bpb.bpbFATs + rds + (u_int64_t) (maxcls(fat) + 1) * bpb.bpbSecPerClust) <= bpb.bpbHugeSectors; bpb.bpbSecPerClust <<= 1) continue; } if (fat != 32 && bpb.bpbBigFATsecs > MAXU16) { warnx("too many sectors/FAT for FAT12/16"); goto done; } x1 = bpb.bpbResSectors + rds; x = bpb.bpbBigFATsecs ? bpb.bpbBigFATsecs : 1; if (x1 + (u_int64_t)x * bpb.bpbFATs > bpb.bpbHugeSectors) { warnx("meta data exceeds file system size"); goto done; } x1 += x * bpb.bpbFATs; x = (u_int64_t)(bpb.bpbHugeSectors - x1) * bpb.bpbBytesPerSec * NPB / (bpb.bpbSecPerClust * bpb.bpbBytesPerSec * NPB + fat / BPN * bpb.bpbFATs); x2 = howmany((RESFTE + MIN(x, maxcls(fat))) * (fat / BPN), bpb.bpbBytesPerSec * NPB); if (set_spf) { if (bpb.bpbBigFATsecs == 0) bpb.bpbBigFATsecs = x2; x1 += (bpb.bpbBigFATsecs - 1) * bpb.bpbFATs; } if (set_res) { /* attempt to align root directory */ alignment = (bpb.bpbResSectors + bpb.bpbBigFATsecs * bpb.bpbFATs) % bpb.bpbSecPerClust; if (o.align) extra_res += bpb.bpbSecPerClust - alignment; } attempts++; } while (o.align && alignment != 0 && attempts < 2); if (o.align && alignment != 0) warnx("warning: Alignment failed."); cls = (bpb.bpbHugeSectors - x1) / bpb.bpbSecPerClust; x = (u_int64_t)bpb.bpbBigFATsecs * bpb.bpbBytesPerSec * NPB / (fat / BPN) - RESFTE; if (cls > x) cls = x; if (bpb.bpbBigFATsecs < x2) warnx("warning: sectors/FAT limits file system to %u clusters", cls); if (cls < mincls(fat)) { warnx("%u clusters too few clusters for FAT%u, need %u", cls, fat, mincls(fat)); goto done; } if (cls > maxcls(fat)) { cls = maxcls(fat); bpb.bpbHugeSectors = x1 + (cls + 1) * bpb.bpbSecPerClust - 1; warnx("warning: FAT type limits file system to %u sectors", bpb.bpbHugeSectors); } printf("%s: %u sector%s in %u FAT%u cluster%s " "(%u bytes/cluster)\n", fname, cls * bpb.bpbSecPerClust, cls * bpb.bpbSecPerClust == 1 ? "" : "s", cls, fat, cls == 1 ? "" : "s", bpb.bpbBytesPerSec * bpb.bpbSecPerClust); if (!bpb.bpbMedia) bpb.bpbMedia = !bpb.bpbHiddenSecs ? 0xf0 : 0xf8; if (fat == 32) bpb.bpbRootClust = RESFTE; if (bpb.bpbHugeSectors <= MAXU16) { bpb.bpbSectors = bpb.bpbHugeSectors; bpb.bpbHugeSectors = 0; } if (fat != 32) { bpb.bpbFATsecs = bpb.bpbBigFATsecs; bpb.bpbBigFATsecs = 0; } print_bpb(&bpb); if (!o.no_create) { if (o.timestamp_set) { tv.tv_sec = now = o.timestamp; tv.tv_usec = 0; tm = gmtime(&now); } else { gettimeofday(&tv, NULL); now = tv.tv_sec; tm = localtime(&now); } chunksize = getchunksize(); physbuf = malloc(chunksize); if (physbuf == NULL) { warn(NULL); goto done; } physbuf_end = physbuf + chunksize; img = physbuf; dir = bpb.bpbResSectors + (bpb.bpbFATsecs ? bpb.bpbFATsecs : bpb.bpbBigFATsecs) * bpb.bpbFATs; memset(&si_sa, 0, sizeof(si_sa)); si_sa.sa_handler = infohandler; #ifdef SIGINFO if (sigaction(SIGINFO, &si_sa, NULL) == -1) { warn("sigaction SIGINFO"); goto done; } #endif for (lsn = 0; lsn < dir + (fat == 32 ? bpb.bpbSecPerClust : rds); lsn++) { if (got_siginfo) { fprintf(stderr,"%s: writing sector %u of %u (%u%%)\n", fname, lsn, (dir + (fat == 32 ? bpb.bpbSecPerClust: rds)), (lsn * 100) / (dir + (fat == 32 ? bpb.bpbSecPerClust: rds))); got_siginfo = 0; } x = lsn; if (o.bootstrap && fat == 32 && bpb.bpbBackup != MAXU16 && bss <= bpb.bpbBackup && x >= bpb.bpbBackup) { x -= bpb.bpbBackup; if (!x && lseek(fd1, o.offset, SEEK_SET)) { warn("%s", bname); goto done; } } if (o.bootstrap && x < bss) { if ((n = read(fd1, img, bpb.bpbBytesPerSec)) == -1) { warn("%s", bname); goto done; } if ((unsigned)n != bpb.bpbBytesPerSec) { warnx("%s: can't read sector %u", bname, x); goto done; } } else memset(img, 0, bpb.bpbBytesPerSec); if (!lsn || (fat == 32 && bpb.bpbBackup != MAXU16 && lsn == bpb.bpbBackup)) { x1 = sizeof(struct bs); bsbpb = (struct bsbpb *)(img + x1); mk2(bsbpb->bpbBytesPerSec, bpb.bpbBytesPerSec); mk1(bsbpb->bpbSecPerClust, bpb.bpbSecPerClust); mk2(bsbpb->bpbResSectors, bpb.bpbResSectors); mk1(bsbpb->bpbFATs, bpb.bpbFATs); mk2(bsbpb->bpbRootDirEnts, bpb.bpbRootDirEnts); mk2(bsbpb->bpbSectors, bpb.bpbSectors); mk1(bsbpb->bpbMedia, bpb.bpbMedia); mk2(bsbpb->bpbFATsecs, bpb.bpbFATsecs); mk2(bsbpb->bpbSecPerTrack, bpb.bpbSecPerTrack); mk2(bsbpb->bpbHeads, bpb.bpbHeads); mk4(bsbpb->bpbHiddenSecs, bpb.bpbHiddenSecs); mk4(bsbpb->bpbHugeSectors, bpb.bpbHugeSectors); x1 += sizeof(struct bsbpb); if (fat == 32) { bsxbpb = (struct bsxbpb *)(img + x1); mk4(bsxbpb->bpbBigFATsecs, bpb.bpbBigFATsecs); mk2(bsxbpb->bpbExtFlags, 0); mk2(bsxbpb->bpbFSVers, 0); mk4(bsxbpb->bpbRootClust, bpb.bpbRootClust); mk2(bsxbpb->bpbFSInfo, bpb.bpbFSInfo); mk2(bsxbpb->bpbBackup, bpb.bpbBackup); x1 += sizeof(struct bsxbpb); } bsx = (struct bsx *)(img + x1); mk1(bsx->exBootSignature, 0x29); if (o.volume_id_set) x = o.volume_id; else x = (((u_int)(1 + tm->tm_mon) << 8 | (u_int)tm->tm_mday) + ((u_int)tm->tm_sec << 8 | (u_int)(tv.tv_usec / 10))) << 16 | ((u_int)(1900 + tm->tm_year) + ((u_int)tm->tm_hour << 8 | (u_int)tm->tm_min)); mk4(bsx->exVolumeID, x); mklabel(bsx->exVolumeLabel, o.volume_label ? o.volume_label : "NO NAME"); snprintf(buf, sizeof(buf), "FAT%u", fat); setstr(bsx->exFileSysType, buf, sizeof(bsx->exFileSysType)); if (!o.bootstrap) { x1 += sizeof(struct bsx); bs = (struct bs *)img; mk1(bs->bsJump[0], 0xeb); mk1(bs->bsJump[1], x1 - 2); mk1(bs->bsJump[2], 0x90); setstr(bs->bsOemName, o.OEM_string ? o.OEM_string : "BSD4.4 ", sizeof(bs->bsOemName)); memcpy(img + x1, bootcode, sizeof(bootcode)); mk2(img + MINBPS - 2, DOSMAGIC); } } else if (fat == 32 && bpb.bpbFSInfo != MAXU16 && (lsn == bpb.bpbFSInfo || (bpb.bpbBackup != MAXU16 && lsn == bpb.bpbBackup + bpb.bpbFSInfo))) { mk4(img, 0x41615252); mk4(img + MINBPS - 28, 0x61417272); mk4(img + MINBPS - 24, 0xffffffff); mk4(img + MINBPS - 20, 0xffffffff); mk2(img + MINBPS - 2, DOSMAGIC); } else if (lsn >= bpb.bpbResSectors && lsn < dir && !((lsn - bpb.bpbResSectors) % (bpb.bpbFATsecs ? bpb.bpbFATsecs : bpb.bpbBigFATsecs))) { mk1(img[0], bpb.bpbMedia); for (x = 1; x < fat * (fat == 32 ? 3 : 2) / 8; x++) mk1(img[x], fat == 32 && x % 4 == 3 ? 0x0f : 0xff); } else if (lsn == dir && o.volume_label) { de = (struct de *)img; mklabel(de->deName, o.volume_label); mk1(de->deAttributes, 050); x = (u_int)tm->tm_hour << 11 | (u_int)tm->tm_min << 5 | (u_int)tm->tm_sec >> 1; mk2(de->deMTime, x); x = (u_int)(tm->tm_year - 80) << 9 | (u_int)(tm->tm_mon + 1) << 5 | (u_int)tm->tm_mday; mk2(de->deMDate, x); } /* * Issue a write of chunksize once we have collected * enough sectors. */ img += bpb.bpbBytesPerSec; if (img >= physbuf_end) { n = write(fd, physbuf, chunksize); if (n != chunksize) { warnx("%s: can't write sector %u", fname, lsn); goto done; } img = physbuf; } } /* * Write remaining sectors, if the last write didn't end * up filling a whole chunk. */ if (img != physbuf) { ssize_t tailsize = img - physbuf; n = write(fd, physbuf, tailsize); if (n != tailsize) { warnx("%s: can't write sector %u", fname, lsn); goto done; } } } rv = 0; done: free(physbuf); if (fd != -1) close(fd); if (fd1 != -1) close(fd1); return rv; } /* * return -1 with error if file system is mounted. */ #ifndef MAKEFS static int check_mounted(const char *fname, mode_t mode) { /* * If getmntinfo() is not available (e.g. Linux) don't check. This should * not be a problem since we will only be using makefs to create images. */ struct statfs *mp; const char *s1, *s2; size_t len; int n, r; if (!(n = getmntinfo(&mp, MNT_NOWAIT))) { warn("getmntinfo"); return -1; } len = strlen(_PATH_DEV); s1 = fname; if (!strncmp(s1, _PATH_DEV, len)) s1 += len; r = S_ISCHR(mode) && s1 != fname && *s1 == 'r'; for (; n--; mp++) { s2 = mp->f_mntfromname; if (!strncmp(s2, _PATH_DEV, len)) s2 += len; if ((r && s2 != mp->f_mntfromname && !strcmp(s1 + 1, s2)) || !strcmp(s1, s2)) { warnx("%s is mounted on %s", fname, mp->f_mntonname); return -1; } } return 0; } #endif /* * Get optimal I/O size */ static ssize_t getchunksize(void) { static ssize_t chunksize; if (chunksize != 0) return (chunksize); #ifdef KERN_MAXPHYS int mib[2]; size_t len; mib[0] = CTL_KERN; mib[1] = KERN_MAXPHYS; len = sizeof(chunksize); if (sysctl(mib, 2, &chunksize, &len, NULL, 0) == -1) { warn("sysctl: KERN_MAXPHYS, using %zu", (size_t)MAXPHYS); chunksize = 0; } #endif if (chunksize == 0) chunksize = MAXPHYS; /* * For better performance, we want to write larger chunks instead of * individual sectors (the size can only be 512, 1024, 2048 or 4096 * bytes). Assert that chunksize can always hold an integer number of * sectors by asserting that both are power of two numbers and the * chunksize is greater than MAXBPS. */ static_assert(powerof2(MAXBPS), "MAXBPS is not power of 2"); assert(powerof2(chunksize)); assert(chunksize > MAXBPS); return (chunksize); } /* * Get a standard format. */ static int getstdfmt(const char *fmt, struct bpb *bpb) { u_int x, i; x = nitems(stdfmt); for (i = 0; i < x && strcmp(fmt, stdfmt[i].name); i++); if (i == x) { warnx("%s: unknown standard format", fmt); return -1; } *bpb = stdfmt[i].bpb; return 0; } static void compute_geometry_from_file(int fd, const char *fname, struct disklabel *lp) { struct stat st; off_t ms; if (fstat(fd, &st)) err(1, "cannot get disk size"); if (!S_ISREG(st.st_mode)) errx(1, "%s is not a regular file", fname); ms = st.st_size; lp->d_secsize = 512; lp->d_nsectors = 63; lp->d_ntracks = 255; lp->d_secperunit = ms / lp->d_secsize; } /* * Get disk slice, partition, and geometry information. */ static int getdiskinfo(int fd, const char *fname, const char *dtype, __unused int oflag, struct bpb *bpb) { struct disklabel *lp, dlp; off_t hs = 0; #ifndef MAKEFS off_t ms; struct fd_type type; lp = NULL; /* If the user specified a disk type, try to use that */ if (dtype != NULL) { lp = getdiskbyname(dtype); } /* Maybe it's a floppy drive */ if (lp == NULL) { if (ioctl(fd, DIOCGMEDIASIZE, &ms) == -1) { /* create a fake geometry for a file image */ compute_geometry_from_file(fd, fname, &dlp); lp = &dlp; } else if (ioctl(fd, FD_GTYPE, &type) != -1) { dlp.d_secsize = 128 << type.secsize; dlp.d_nsectors = type.sectrac; dlp.d_ntracks = type.heads; dlp.d_secperunit = ms / dlp.d_secsize; lp = &dlp; } } /* Maybe it's a fixed drive */ if (lp == NULL) { if (bpb->bpbBytesPerSec) dlp.d_secsize = bpb->bpbBytesPerSec; if (bpb->bpbBytesPerSec == 0 && ioctl(fd, DIOCGSECTORSIZE, &dlp.d_secsize) == -1) err(1, "cannot get sector size"); dlp.d_secperunit = ms / dlp.d_secsize; if (bpb->bpbSecPerTrack == 0 && ioctl(fd, DIOCGFWSECTORS, &dlp.d_nsectors) == -1) { warn("cannot get number of sectors per track"); dlp.d_nsectors = 63; } if (bpb->bpbHeads == 0 && ioctl(fd, DIOCGFWHEADS, &dlp.d_ntracks) == -1) { warn("cannot get number of heads"); if (dlp.d_secperunit <= 63*1*1024) dlp.d_ntracks = 1; else if (dlp.d_secperunit <= 63*16*1024) dlp.d_ntracks = 16; else dlp.d_ntracks = 255; } hs = (ms / dlp.d_secsize) - dlp.d_secperunit; lp = &dlp; } #else (void)dtype; /* In the makefs case we only support image files: */ compute_geometry_from_file(fd, fname, &dlp); lp = &dlp; #endif if (bpb->bpbBytesPerSec == 0) { if (ckgeom(fname, lp->d_secsize, "bytes/sector") == -1) return -1; bpb->bpbBytesPerSec = lp->d_secsize; } if (bpb->bpbSecPerTrack == 0) { if (ckgeom(fname, lp->d_nsectors, "sectors/track") == -1) return -1; bpb->bpbSecPerTrack = lp->d_nsectors; } if (bpb->bpbHeads == 0) { if (ckgeom(fname, lp->d_ntracks, "drive heads") == -1) return -1; bpb->bpbHeads = lp->d_ntracks; } if (bpb->bpbHugeSectors == 0) bpb->bpbHugeSectors = lp->d_secperunit; if (bpb->bpbHiddenSecs == 0) bpb->bpbHiddenSecs = hs; return 0; } /* * Print out BPB values. */ static void print_bpb(struct bpb *bpb) { printf("BytesPerSec=%u SecPerClust=%u ResSectors=%u FATs=%u", bpb->bpbBytesPerSec, bpb->bpbSecPerClust, bpb->bpbResSectors, bpb->bpbFATs); if (bpb->bpbRootDirEnts) printf(" RootDirEnts=%u", bpb->bpbRootDirEnts); if (bpb->bpbSectors) printf(" Sectors=%u", bpb->bpbSectors); printf(" Media=%#x", bpb->bpbMedia); if (bpb->bpbFATsecs) printf(" FATsecs=%u", bpb->bpbFATsecs); printf(" SecPerTrack=%u Heads=%u HiddenSecs=%u", bpb->bpbSecPerTrack, bpb->bpbHeads, bpb->bpbHiddenSecs); if (bpb->bpbHugeSectors) printf(" HugeSectors=%u", bpb->bpbHugeSectors); if (!bpb->bpbFATsecs) { printf(" FATsecs=%u RootCluster=%u", bpb->bpbBigFATsecs, bpb->bpbRootClust); printf(" FSInfo="); printf(bpb->bpbFSInfo == MAXU16 ? "%#x" : "%u", bpb->bpbFSInfo); printf(" Backup="); printf(bpb->bpbBackup == MAXU16 ? "%#x" : "%u", bpb->bpbBackup); } printf("\n"); } /* * Check a disk geometry value. */ static int ckgeom(const char *fname, u_int val, const char *msg) { if (!val) { warnx("%s: no default %s", fname, msg); return -1; } if (val > MAXU16) { warnx("%s: illegal %s %d", fname, msg, val); return -1; } return 0; } /* * Check a volume label. */ static int oklabel(const char *src) { int c, i; for (i = 0; i <= 11; i++) { c = (u_char)*src++; if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c)) break; } return i && !c; } /* * Make a volume label. */ static void mklabel(u_int8_t *dest, const char *src) { int c, i; for (i = 0; i < 11; i++) { c = *src ? toupper(*src++) : ' '; *dest++ = !i && c == '\xe5' ? 5 : c; } } /* * Copy string, padding with spaces. */ static void setstr(u_int8_t *dest, const char *src, size_t len) { while (len--) *dest++ = *src ? *src++ : ' '; } static void infohandler(int sig __unused) { got_siginfo = 1; } diff --git a/sbin/newfs_msdos/newfs_msdos.c b/sbin/newfs_msdos/newfs_msdos.c index 1ba399fe447e..312a862d9113 100644 --- a/sbin/newfs_msdos/newfs_msdos.c +++ b/sbin/newfs_msdos/newfs_msdos.c @@ -1,281 +1,276 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 1998 Robert Nordier * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include "mkfs_msdos.h" #define argto1(arg, lo, msg) argtou(arg, lo, 0xff, msg) #define argto2(arg, lo, msg) argtou(arg, lo, 0xffff, msg) #define argto4(arg, lo, msg) argtou(arg, lo, 0xffffffff, msg) #define argtox(arg, lo, msg) argtou(arg, lo, UINT_MAX, msg) static u_int argtou(const char *, u_int, u_int, const char *); static off_t argtooff(const char *, const char *); static void usage(void) __dead2; static time_t get_tstamp(const char *b) { struct stat st; char *eb; long long l; if (stat(b, &st) != -1) return (time_t)st.st_mtime; errno = 0; l = strtoll(b, &eb, 0); if (b == eb || *eb || errno) errx(EXIT_FAILURE, "Can't parse timestamp '%s'", b); return (time_t)l; } /* * Construct a FAT12, FAT16, or FAT32 file system. */ int main(int argc, char *argv[]) { static const char opts[] = "@:NAB:C:F:I:L:O:S:a:b:c:e:f:h:i:k:m:n:o:r:s:T:u:"; struct msdos_options o; const char *fname, *dtype; char buf[MAXPATHLEN]; int ch; memset(&o, 0, sizeof(o)); while ((ch = getopt(argc, argv, opts)) != -1) switch (ch) { case '@': o.offset = argtooff(optarg, "offset"); break; case 'N': o.no_create = 1; break; case 'A': o.align = true; break; case 'B': o.bootstrap = optarg; break; case 'C': o.create_size = argtooff(optarg, "create size"); break; case 'F': if (strcmp(optarg, "12") && strcmp(optarg, "16") && strcmp(optarg, "32")) errx(1, "%s: bad FAT type", optarg); o.fat_type = atoi(optarg); break; case 'I': o.volume_id = argto4(optarg, 0, "volume ID"); o.volume_id_set = 1; break; case 'L': o.volume_label = optarg; break; case 'O': o.OEM_string = optarg; break; case 'S': o.bytes_per_sector = argto2(optarg, 1, "bytes/sector"); break; case 'a': o.sectors_per_fat = argto4(optarg, 1, "sectors/FAT"); break; case 'b': o.block_size = argtox(optarg, 1, "block size"); o.sectors_per_cluster = 0; break; case 'c': o.sectors_per_cluster = argto1(optarg, 1, "sectors/cluster"); o.block_size = 0; break; case 'e': o.directory_entries = argto2(optarg, 1, "directory entries"); break; case 'f': o.floppy = optarg; break; case 'h': o.drive_heads = argto2(optarg, 1, "drive heads"); break; case 'i': o.info_sector = argto2(optarg, 1, "info sector"); break; case 'k': o.backup_sector = argto2(optarg, 1, "backup sector"); break; case 'm': o.media_descriptor = argto1(optarg, 0, "media descriptor"); o.media_descriptor_set = 1; break; case 'n': o.num_FAT = argto1(optarg, 1, "number of FATs"); break; case 'o': o.hidden_sectors = argto4(optarg, 0, "hidden sectors"); o.hidden_sectors_set = 1; break; case 'r': o.reserved_sectors = argto2(optarg, 1, "reserved sectors"); break; case 's': o.size = argto4(optarg, 1, "file system size"); break; case 'T': o.timestamp_set = 1; o.timestamp = get_tstamp(optarg); break; case 'u': o.sectors_per_track = argto2(optarg, 1, "sectors/track"); break; default: usage(); } argc -= optind; argv += optind; if (argc < 1 || argc > 2) usage(); if (o.align) { if (o.reserved_sectors) errx(1, "align (-A) is incompatible with -r"); } fname = *argv++; if (!o.create_size && !strchr(fname, '/')) { snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname); fname = buf; } dtype = *argv; exit(!!mkfs_msdos(fname, dtype, &o)); } /* * Convert and check a numeric option argument. */ static u_int argtou(const char *arg, u_int lo, u_int hi, const char *msg) { char *s; u_long x; errno = 0; x = strtoul(arg, &s, 0); if (errno || !*arg || *s || x < lo || x > hi) errx(1, "%s: bad %s", arg, msg); return x; } /* * Same for off_t, with optional skmgpP suffix */ static off_t argtooff(const char *arg, const char *msg) { char *s; off_t x; errno = 0; x = strtoll(arg, &s, 0); /* allow at most one extra char */ if (errno || x < 0 || (s[0] && s[1]) ) errx(1, "%s: bad %s", arg, msg); if (*s) { /* the extra char is the multiplier */ switch (*s) { default: errx(1, "%s: bad %s", arg, msg); /* notreached */ case 's': /* sector */ case 'S': x <<= 9; /* times 512 */ break; case 'k': /* kilobyte */ case 'K': x <<= 10; /* times 1024 */ break; case 'm': /* megabyte */ case 'M': x <<= 20; /* times 1024*1024 */ break; case 'g': /* gigabyte */ case 'G': x <<= 30; /* times 1024*1024*1024 */ break; case 'p': /* partition start */ case 'P': case 'l': /* partition length */ case 'L': errx(1, "%s: not supported yet %s", arg, msg); /* notreached */ } } return x; } /* * Print usage message. */ static void usage(void) { fprintf(stderr, "usage: %s [ -options ] special [disktype]\n", getprogname()); fprintf(stderr, "where the options are:\n"); static struct { char o; const char *h; } opts[] = { #define AOPT(_opt, _type, _name, _min, _desc) { _opt, _desc }, ALLOPTS #undef AOPT }; for (size_t i = 0; i < nitems(opts); i++) fprintf(stderr, "\t-%c %s\n", opts[i].o, opts[i].h); exit(1); } diff --git a/sbin/nos-tun/nos-tun.c b/sbin/nos-tun/nos-tun.c index 509f928a2bb8..f3af62db265b 100644 --- a/sbin/nos-tun/nos-tun.c +++ b/sbin/nos-tun/nos-tun.c @@ -1,400 +1,395 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 1996, Nickolay Dudorov * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* * 'nos-tun' program configure tunN interface as a point-to-point * connection with two "pseudo"-addresses between this host and * 'target'. * * It uses Ip-over-Ip incapsulation ( protocol number 94 - IPIP) * (known as NOS-incapsulation in CISCO-routers' terminology). * * 'nos-tun' can works with itself and CISCO-routers. * (It may also work with Linux 'nos-tun's, but * I have no Linux system here to test with). * * BUGS (or features ?): * - you must specify ONE of the target host's addresses * ( nos-tun sends and accepts packets only to/from this * address ) * - there can be only ONE tunnel between two hosts, * more precisely - between given host and (one of) * target hosts' address(es) * (and why do you want more ?) */ /* * Mar. 23 1999 by Isao SEKI * I added a new flag for ip protocol number. * We are using 4 as protocol number in ampr.org. * */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Tunnel interface configuration stuff */ static struct ifaliasreq ifra; static struct ifreq ifrq; /* Global descriptors */ int net; /* socket descriptor */ int tun; /* tunnel descriptor */ static void usage(void) __dead2; static int Set_address(char *addr, struct sockaddr_in *sin) { struct hostent *hp; bzero((char *)sin, sizeof(struct sockaddr)); sin->sin_family = AF_INET; if((sin->sin_addr.s_addr = inet_addr(addr)) == INADDR_NONE) { hp = gethostbyname(addr); if (!hp) { syslog(LOG_ERR,"unknown host %s", addr); return 1; } sin->sin_family = hp->h_addrtype; bcopy(hp->h_addr, (caddr_t)&sin->sin_addr, hp->h_length); } return 0; } static int tun_open(char *dev_name, struct sockaddr *ouraddr, char *theiraddr) { int s; struct sockaddr_in *sin; /* Open tun device */ tun = open(dev_name, O_RDWR); if (tun < 0) { syslog(LOG_ERR,"can't open %s - %m", dev_name); return(1); } /* * At first, name the interface. */ bzero((char *)&ifra, sizeof(ifra)); bzero((char *)&ifrq, sizeof(ifrq)); strncpy(ifrq.ifr_name, dev_name+5, IFNAMSIZ); strncpy(ifra.ifra_name, dev_name+5, IFNAMSIZ); s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { syslog(LOG_ERR,"can't open socket - %m"); goto tunc_return; } /* * Delete (previous) addresses for interface * * !!!! * On FreeBSD this ioctl returns error * when tunN have no addresses, so - log and ignore it. * */ if (ioctl(s, SIOCDIFADDR, &ifra) < 0) { syslog(LOG_ERR,"SIOCDIFADDR - %m"); } /* * Set interface address */ sin = (struct sockaddr_in *)&(ifra.ifra_addr); bcopy(ouraddr, sin, sizeof(struct sockaddr_in)); sin->sin_len = sizeof(*sin); /* * Set destination address */ sin = (struct sockaddr_in *)&(ifra.ifra_broadaddr); if(Set_address(theiraddr,sin)) { syslog(LOG_ERR,"bad destination address: %s",theiraddr); goto stunc_return; } sin->sin_len = sizeof(*sin); if (ioctl(s, SIOCAIFADDR, &ifra) < 0) { syslog(LOG_ERR,"can't set interface address - %m"); goto stunc_return; } /* * Now, bring up the interface. */ if (ioctl(s, SIOCGIFFLAGS, &ifrq) < 0) { syslog(LOG_ERR,"can't get interface flags - %m"); goto stunc_return; } ifrq.ifr_flags |= IFF_UP; if (!(ioctl(s, SIOCSIFFLAGS, &ifrq) < 0)) { close(s); return(0); } syslog(LOG_ERR,"can't set interface UP - %m"); stunc_return: close(s); tunc_return: close(tun); return(1); } static void Finish(int signum) { int s; syslog(LOG_INFO,"exiting"); close(net); s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { syslog(LOG_ERR,"can't open socket - %m"); goto closing_tun; } /* * Shut down interface. */ if (ioctl(s, SIOCGIFFLAGS, &ifrq) < 0) { syslog(LOG_ERR,"can't get interface flags - %m"); goto closing_fds; } ifrq.ifr_flags &= ~(IFF_UP|IFF_RUNNING); if (ioctl(s, SIOCSIFFLAGS, &ifrq) < 0) { syslog(LOG_ERR,"can't set interface DOWN - %m"); goto closing_fds; } /* * Delete addresses for interface */ bzero(&ifra.ifra_addr, sizeof(ifra.ifra_addr)); bzero(&ifra.ifra_broadaddr, sizeof(ifra.ifra_addr)); bzero(&ifra.ifra_mask, sizeof(ifra.ifra_addr)); if (ioctl(s, SIOCDIFADDR, &ifra) < 0) { syslog(LOG_ERR,"can't delete interface's addresses - %m"); } closing_fds: close(s); closing_tun: close(tun); closelog(); exit(signum); } int main (int argc, char **argv) { int c, len, ipoff; char *dev_name = NULL; char *point_to = NULL; char *to_point = NULL; char *target; char *source = NULL; char *protocol = NULL; int protnum; struct sockaddr t_laddr; /* Source address of tunnel */ struct sockaddr whereto; /* Destination of tunnel */ struct sockaddr wherefrom; /* Source of tunnel */ struct sockaddr_in *to; char buf[0x2000]; /* Packets buffer */ struct ip *ip = (struct ip *)buf; fd_set rfds; /* File descriptors for select() */ int nfds; /* Return from select() */ int lastfd; /* highest fd we care about */ while ((c = getopt(argc, argv, "d:s:t:p:")) != -1) { switch (c) { case 'd': to_point = optarg; break; case 's': point_to = optarg; break; case 't': dev_name = optarg; break; case 'p': protocol = optarg; break; } } argc -= optind; argv += optind; if ((argc != 1 && argc != 2) || (dev_name == NULL) || (point_to == NULL) || (to_point == NULL)) { usage(); } if(protocol == NULL) protnum = 94; else protnum = atoi(protocol); if (argc == 1) { target = *argv; } else { source = *argv++; target = *argv; } /* Establish logging through 'syslog' */ openlog("nos-tun", LOG_PID, LOG_DAEMON); if(Set_address(point_to, (struct sockaddr_in *)&t_laddr)) { closelog(); exit(2); } if(tun_open(dev_name, &t_laddr, to_point)) { closelog(); exit(3); } to = (struct sockaddr_in *)&whereto; if(Set_address(target, to)) Finish(4); if ((net = socket(AF_INET, SOCK_RAW, protnum)) < 0) { syslog(LOG_ERR,"can't open socket - %m"); Finish(5); } if (source) { if (Set_address(source, (struct sockaddr_in *)&wherefrom)) Finish(9); if (bind(net, &wherefrom, sizeof(wherefrom)) < 0) { syslog(LOG_ERR, "can't bind source address - %m"); Finish(10); } } if (connect(net,&whereto,sizeof(struct sockaddr_in)) < 0 ) { syslog(LOG_ERR,"can't connect to target - %m"); close(net); Finish(6); } /* Demonize it */ daemon(0,0); /* Install signal handlers */ (void)signal(SIGHUP,Finish); (void)signal(SIGINT,Finish); (void)signal(SIGTERM,Finish); if (tun > net) lastfd = tun; else lastfd = net; for (;;) { /* Set file descriptors for select() */ FD_ZERO(&rfds); FD_SET(tun,&rfds); FD_SET(net,&rfds); nfds = select(lastfd+1,&rfds,NULL,NULL,NULL); if(nfds < 0) { syslog(LOG_ERR,"interrupted select"); close(net); Finish(7); } if(nfds == 0) { /* Impossible ? */ syslog(LOG_ERR,"timeout in select"); close(net); Finish(8); } if(FD_ISSET(net,&rfds)) { /* Read from socket ... */ len = read(net, buf, sizeof(buf)); /* Check if this is "our" packet */ if((ip->ip_src).s_addr == (to->sin_addr).s_addr) { /* ... skip encapsulation headers ... */ ipoff = (ip->ip_hl << 2); /* ... and write to tun-device */ write(tun,buf+ipoff,len-ipoff); } } if(FD_ISSET(tun,&rfds)) { /* Read from tun ... */ len = read(tun, buf, sizeof(buf)); /* ... and send to network */ if(send(net, buf, len,0) <= 0) { syslog(LOG_ERR,"can't send - %m"); } } } } static void usage(void) { fprintf(stderr, "usage: nos-tun -t tunnel -s source -d destination -p protocol_number [source] target\n"); exit(1); } diff --git a/sbin/restore/dirs.c b/sbin/restore/dirs.c index 4a25b728e8a0..db9e05a0bc50 100644 --- a/sbin/restore/dirs.c +++ b/sbin/restore/dirs.c @@ -1,820 +1,818 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)dirs.c 8.7 (Berkeley) 5/1/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "restore.h" #include "extern.h" /* * Symbol table of directories read from tape. */ #define HASHSIZE 1000 #define INOHASH(val) (val % HASHSIZE) struct inotab { struct inotab *t_next; ino_t t_ino; int32_t t_seekpt; int32_t t_size; }; static struct inotab *inotab[HASHSIZE]; /* * Information retained about directories. */ struct modeinfo { ino_t ino; struct timespec ctimep[2]; struct timespec mtimep[2]; mode_t mode; uid_t uid; gid_t gid; u_int flags; int extsize; }; /* * Definitions for library routines operating on directories. */ #undef DIRBLKSIZ #define DIRBLKSIZ 1024 struct rstdirdesc { int dd_fd; int32_t dd_loc; int32_t dd_size; char dd_buf[DIRBLKSIZ]; }; /* * Global variables for this file. */ static long seekpt; static FILE *df, *mf; static RST_DIR *dirp; static char dirfile[MAXPATHLEN] = "#"; /* No file */ static char modefile[MAXPATHLEN] = "#"; /* No file */ static char dot[2] = "."; /* So it can be modified */ static struct inotab *allocinotab(struct context *, long); static void flushent(void); static struct inotab *inotablookup(ino_t); static RST_DIR *opendirfile(const char *); static void putdir(char *, size_t); static void putdirattrs(char *, size_t); static void putent(struct direct *); static void rst_seekdir(RST_DIR *, long, long); static long rst_telldir(RST_DIR *); static struct direct *searchdir(ino_t, char *); static void fail_dirtmp(char *); /* * Extract directory contents, building up a directory structure * on disk for extraction by name. * If genmode is requested, save mode, owner, and times for all * directories on the tape. */ void extractdirs(int genmode) { struct inotab *itp; struct direct nulldir; int i, fd; const char *tmpdir; vprintf(stdout, "Extract directories from tape\n"); if ((tmpdir = getenv("TMPDIR")) == NULL || tmpdir[0] == '\0') tmpdir = _PATH_TMP; (void) snprintf(dirfile, sizeof(dirfile), "%s/rstdir%jd", tmpdir, (intmax_t)dumpdate); if (command != 'r' && command != 'R') { (void) strcat(dirfile, "-XXXXXX"); fd = mkstemp(dirfile); } else fd = open(dirfile, O_RDWR|O_CREAT|O_EXCL, 0666); if (fd == -1 || (df = fdopen(fd, "w")) == NULL) { if (fd != -1) close(fd); warn("%s: cannot create directory database", dirfile); done(1); } if (genmode != 0) { (void) snprintf(modefile, sizeof(modefile), "%s/rstmode%jd", tmpdir, (intmax_t)dumpdate); if (command != 'r' && command != 'R') { (void) strcat(modefile, "-XXXXXX"); fd = mkstemp(modefile); } else fd = open(modefile, O_RDWR|O_CREAT|O_EXCL, 0666); if (fd == -1 || (mf = fdopen(fd, "w")) == NULL) { if (fd != -1) close(fd); warn("%s: cannot create modefile", modefile); done(1); } } nulldir.d_ino = 0; nulldir.d_type = DT_DIR; nulldir.d_namlen = 1; (void) strcpy(nulldir.d_name, "/"); nulldir.d_reclen = DIRSIZ(0, &nulldir); for (;;) { curfile.name = ""; curfile.action = USING; if (curfile.mode == 0 || (curfile.mode & IFMT) != IFDIR) break; itp = allocinotab(&curfile, seekpt); getfile(putdir, putdirattrs, xtrnull); putent(&nulldir); flushent(); itp->t_size = seekpt - itp->t_seekpt; } if (fclose(df) != 0) fail_dirtmp(dirfile); dirp = opendirfile(dirfile); if (dirp == NULL) fprintf(stderr, "opendirfile: %s\n", strerror(errno)); if (mf != NULL && fclose(mf) != 0) fail_dirtmp(modefile); i = dirlookup(dot); if (i == 0) panic("Root directory is not on tape\n"); } /* * skip over all the directories on the tape */ void skipdirs(void) { while (curfile.ino && (curfile.mode & IFMT) == IFDIR) { skipfile(); } } /* * Recursively find names and inumbers of all files in subtree * pname and pass them off to be processed. */ void treescan(char *pname, ino_t ino, long (*todo)(char *, ino_t, int)) { struct inotab *itp; struct direct *dp; int namelen; long bpt; char locname[MAXPATHLEN]; itp = inotablookup(ino); if (itp == NULL) { /* * Pname is name of a simple file or an unchanged directory. */ (void) (*todo)(pname, ino, LEAF); return; } /* * Pname is a dumped directory name. */ if ((*todo)(pname, ino, NODE) == FAIL) return; /* * begin search through the directory * skipping over "." and ".." */ (void) strlcpy(locname, pname, sizeof(locname)); (void) strlcat(locname, "/", sizeof(locname)); namelen = strlen(locname); rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); dp = rst_readdir(dirp); /* "." */ if (dp != NULL && strcmp(dp->d_name, ".") == 0) dp = rst_readdir(dirp); /* ".." */ else fprintf(stderr, "Warning: `.' missing from directory %s\n", pname); if (dp != NULL && strcmp(dp->d_name, "..") == 0) dp = rst_readdir(dirp); /* first real entry */ else fprintf(stderr, "Warning: `..' missing from directory %s\n", pname); bpt = rst_telldir(dirp); /* * a zero inode signals end of directory */ while (dp != NULL) { locname[namelen] = '\0'; if (namelen + dp->d_namlen >= sizeof(locname)) { fprintf(stderr, "%s%s: name exceeds %zu char\n", locname, dp->d_name, sizeof(locname) - 1); } else { (void)strlcat(locname, dp->d_name, sizeof(locname)); treescan(locname, dp->d_ino, todo); rst_seekdir(dirp, bpt, itp->t_seekpt); } dp = rst_readdir(dirp); bpt = rst_telldir(dirp); } } /* * Lookup a pathname which is always assumed to start from the UFS_ROOTINO. */ struct direct * pathsearch(const char *pathname) { ino_t ino; struct direct *dp; char *path, *name, buffer[MAXPATHLEN]; strcpy(buffer, pathname); path = buffer; ino = UFS_ROOTINO; while (*path == '/') path++; dp = NULL; while ((name = strsep(&path, "/")) != NULL && *name != '\0') { if ((dp = searchdir(ino, name)) == NULL) return (NULL); ino = dp->d_ino; } return (dp); } /* * Lookup the requested name in directory inum. * Return its inode number if found, zero if it does not exist. */ static struct direct * searchdir(ino_t inum, char *name) { struct direct *dp; struct inotab *itp; int len; itp = inotablookup(inum); if (itp == NULL) return (NULL); rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); len = strlen(name); do { dp = rst_readdir(dirp); if (dp == NULL) return (NULL); } while (dp->d_namlen != len || strncmp(dp->d_name, name, len) != 0); return (dp); } /* * Put the directory entries in the directory file */ static void putdir(char *buf, size_t size) { struct direct *dp; size_t loc, i; for (loc = 0; loc < size; ) { dp = (struct direct *)(buf + loc); if (Bcvt) swabst((u_char *)"ls", (u_char *) dp); if (oldinofmt && dp->d_ino != 0) { #if BYTE_ORDER == BIG_ENDIAN if (Bcvt) dp->d_namlen = dp->d_type; #else if (!Bcvt && dp->d_namlen == 0) dp->d_namlen = dp->d_type; #endif dp->d_type = DT_UNKNOWN; } i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1)); if ((dp->d_reclen & 0x3) != 0 || dp->d_reclen > i || dp->d_reclen < DIRSIZ(0, dp) #if NAME_MAX < 255 || dp->d_namlen > NAME_MAX #endif ) { vprintf(stdout, "Mangled directory: "); if ((dp->d_reclen & 0x3) != 0) vprintf(stdout, "reclen not multiple of 4 "); if (dp->d_reclen < DIRSIZ(0, dp)) vprintf(stdout, "reclen less than DIRSIZ (%u < %zu) ", dp->d_reclen, DIRSIZ(0, dp)); #if NAME_MAX < 255 if (dp->d_namlen > NAME_MAX) vprintf(stdout, "reclen name too big (%u > %u) ", dp->d_namlen, NAME_MAX); #endif vprintf(stdout, "\n"); loc += i; continue; } loc += dp->d_reclen; if (dp->d_ino != 0) { putent(dp); } } } /* * These variables are "local" to the following two functions. */ char dirbuf[DIRBLKSIZ]; long dirloc = 0; long prev = 0; /* * add a new directory entry to a file. */ static void putent(struct direct *dp) { dp->d_reclen = DIRSIZ(0, dp); if (dirloc + dp->d_reclen > DIRBLKSIZ) { ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev; if (fwrite(dirbuf, DIRBLKSIZ, 1, df) != 1) fail_dirtmp(dirfile); dirloc = 0; } memmove(dirbuf + dirloc, dp, (long)dp->d_reclen); prev = dirloc; dirloc += dp->d_reclen; } /* * flush out a directory that is finished. */ static void flushent(void) { ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev; if (fwrite(dirbuf, (int)dirloc, 1, df) != 1) fail_dirtmp(dirfile); seekpt = ftell(df); dirloc = 0; } /* * Save extended attributes for a directory entry to a file. */ static void putdirattrs(char *buf, size_t size) { if (mf != NULL && fwrite(buf, size, 1, mf) != 1) fail_dirtmp(modefile); } /* * Seek to an entry in a directory. * Only values returned by rst_telldir should be passed to rst_seekdir. * This routine handles many directories in a single file. * It takes the base of the directory in the file, plus * the desired seek offset into it. */ static void rst_seekdir(RST_DIR *dirp, long loc, long base) { if (loc == rst_telldir(dirp)) return; loc -= base; if (loc < 0) fprintf(stderr, "bad seek pointer to rst_seekdir %ld\n", loc); (void) lseek(dirp->dd_fd, base + rounddown2(loc, DIRBLKSIZ), SEEK_SET); dirp->dd_loc = loc & (DIRBLKSIZ - 1); if (dirp->dd_loc != 0) dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ); } /* * get next entry in a directory. */ struct direct * rst_readdir(RST_DIR *dirp) { struct direct *dp; for (;;) { if (dirp->dd_loc == 0) { dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ); if (dirp->dd_size <= 0) { dprintf(stderr, "error reading directory\n"); return (NULL); } } if (dirp->dd_loc >= dirp->dd_size) { dirp->dd_loc = 0; continue; } dp = (struct direct *)(dirp->dd_buf + dirp->dd_loc); if (dp->d_reclen == 0 || dp->d_reclen > DIRBLKSIZ + 1 - dirp->dd_loc) { dprintf(stderr, "corrupted directory: bad reclen %d\n", dp->d_reclen); return (NULL); } dirp->dd_loc += dp->d_reclen; if (dp->d_ino == 0 && strcmp(dp->d_name, "/") == 0) return (NULL); if (dp->d_ino >= maxino) { dprintf(stderr, "corrupted directory: bad inum %d\n", dp->d_ino); continue; } return (dp); } } /* * Simulate the opening of a directory */ void * rst_opendir(const char *name) { struct inotab *itp; RST_DIR *dirp; ino_t ino; if ((ino = dirlookup(name)) > 0 && (itp = inotablookup(ino)) != NULL) { dirp = opendirfile(dirfile); rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); return (dirp); } return (NULL); } /* * In our case, there is nothing to do when closing a directory. */ void rst_closedir(void *arg) { RST_DIR *dirp; dirp = arg; (void)close(dirp->dd_fd); free(dirp); return; } /* * Simulate finding the current offset in the directory. */ static long rst_telldir(RST_DIR *dirp) { return ((long)lseek(dirp->dd_fd, (off_t)0, SEEK_CUR) - dirp->dd_size + dirp->dd_loc); } /* * Open a directory file. */ static RST_DIR * opendirfile(const char *name) { RST_DIR *dirp; int fd; if ((fd = open(name, O_RDONLY)) == -1) return (NULL); if ((dirp = malloc(sizeof(RST_DIR))) == NULL) { (void)close(fd); return (NULL); } dirp->dd_fd = fd; dirp->dd_loc = 0; return (dirp); } /* * Set the mode, owner, and times for all new or changed directories */ void setdirmodes(int flags) { FILE *mf; struct modeinfo node; struct entry *ep; char *cp, *buf; const char *tmpdir; int bufsize; uid_t myuid; vprintf(stdout, "Set directory mode, owner, and times.\n"); if ((tmpdir = getenv("TMPDIR")) == NULL || tmpdir[0] == '\0') tmpdir = _PATH_TMP; if (command == 'r' || command == 'R') (void) snprintf(modefile, sizeof(modefile), "%s/rstmode%jd", tmpdir, (intmax_t)dumpdate); if (modefile[0] == '#') { panic("modefile not defined\n"); fprintf(stderr, "directory mode, owner, and times not set\n"); return; } mf = fopen(modefile, "r"); if (mf == NULL) { fprintf(stderr, "fopen: %s\n", strerror(errno)); fprintf(stderr, "cannot open mode file %s\n", modefile); fprintf(stderr, "directory mode, owner, and times not set\n"); return; } clearerr(mf); bufsize = 0; myuid = getuid(); for (;;) { (void) fread((char *)&node, 1, sizeof(struct modeinfo), mf); if (ferror(mf)) { warn("%s: cannot read modefile.", modefile); fprintf(stderr, "Mode, owner, and times not set.\n"); break; } if (feof(mf)) break; if (node.extsize > 0) { if (bufsize < node.extsize) { if (bufsize > 0) free(buf); if ((buf = malloc(node.extsize)) != NULL) { bufsize = node.extsize; } else { bufsize = 0; } } if (bufsize >= node.extsize) { (void) fread(buf, 1, node.extsize, mf); if (ferror(mf)) { warn("%s: cannot read modefile.", modefile); fprintf(stderr, "Not all external "); fprintf(stderr, "attributes set.\n"); break; } } else { (void) fseek(mf, node.extsize, SEEK_CUR); if (ferror(mf)) { warn("%s: cannot seek in modefile.", modefile); fprintf(stderr, "Not all directory "); fprintf(stderr, "attributes set.\n"); break; } } } ep = lookupino(node.ino); if (command == 'i' || command == 'x') { if (ep == NULL) continue; if ((flags & FORCE) == 0 && ep->e_flags & EXISTED) { ep->e_flags &= ~NEW; continue; } if (node.ino == UFS_ROOTINO && reply("set owner/mode for '.'") == FAIL) continue; } if (ep == NULL) { panic("cannot find directory inode %ju\n", (uintmax_t)node.ino); continue; } cp = myname(ep); if (!Nflag) { if (myuid != 0) (void) chown(cp, myuid, node.gid); else (void) chown(cp, node.uid, node.gid); (void) chmod(cp, node.mode); if (node.extsize > 0) { if (bufsize >= node.extsize) { set_extattr(-1, cp, buf, node.extsize, SXA_FILE); } else { fprintf(stderr, "Cannot restore %s%s\n", "extended attributes for ", cp); } } utimensat(AT_FDCWD, cp, node.ctimep, 0); utimensat(AT_FDCWD, cp, node.mtimep, 0); (void) chflags(cp, node.flags); } ep->e_flags &= ~NEW; } if (bufsize > 0) free(buf); (void) fclose(mf); } /* * Generate a literal copy of a directory. */ int genliteraldir(char *name, ino_t ino) { struct inotab *itp; int ofile, dp, i, size; char buf[BUFSIZ]; itp = inotablookup(ino); if (itp == NULL) panic("Cannot find directory inode %ju named %s\n", (uintmax_t)ino, name); if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) { fprintf(stderr, "%s: ", name); (void) fflush(stderr); fprintf(stderr, "cannot create file: %s\n", strerror(errno)); return (FAIL); } rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); dp = dup(dirp->dd_fd); for (i = itp->t_size; i > 0; i -= BUFSIZ) { size = MIN(i, BUFSIZ); if (read(dp, buf, (int) size) == -1) { fprintf(stderr, "write error extracting inode %ju, name %s\n", (uintmax_t)curfile.ino, curfile.name); fprintf(stderr, "read: %s\n", strerror(errno)); done(1); } if (!Nflag && write(ofile, buf, (int) size) == -1) { fprintf(stderr, "write error extracting inode %ju, name %s\n", (uintmax_t)curfile.ino, curfile.name); fprintf(stderr, "write: %s\n", strerror(errno)); done(1); } } (void) close(dp); (void) close(ofile); return (GOOD); } /* * Determine the type of an inode */ int inodetype(ino_t ino) { struct inotab *itp; itp = inotablookup(ino); if (itp == NULL) return (LEAF); return (NODE); } /* * Allocate and initialize a directory inode entry. * If requested, save its pertinent mode, owner, and time info. */ static struct inotab * allocinotab(struct context *ctxp, long seekpt) { struct inotab *itp; struct modeinfo node; itp = calloc(1, sizeof(struct inotab)); if (itp == NULL) panic("no memory for directory table\n"); itp->t_next = inotab[INOHASH(ctxp->ino)]; inotab[INOHASH(ctxp->ino)] = itp; itp->t_ino = ctxp->ino; itp->t_seekpt = seekpt; if (mf == NULL) return (itp); node.ino = ctxp->ino; node.mtimep[0].tv_sec = ctxp->atime_sec; node.mtimep[0].tv_nsec = ctxp->atime_nsec; node.mtimep[1].tv_sec = ctxp->mtime_sec; node.mtimep[1].tv_nsec = ctxp->mtime_nsec; node.ctimep[0].tv_sec = ctxp->atime_sec; node.ctimep[0].tv_nsec = ctxp->atime_nsec; node.ctimep[1].tv_sec = ctxp->birthtime_sec; node.ctimep[1].tv_nsec = ctxp->birthtime_nsec; node.extsize = ctxp->extsize; node.mode = ctxp->mode; node.flags = ctxp->file_flags; node.uid = ctxp->uid; node.gid = ctxp->gid; if (fwrite((char *)&node, sizeof(struct modeinfo), 1, mf) != 1) fail_dirtmp(modefile); return (itp); } /* * Look up an inode in the table of directories */ static struct inotab * inotablookup(ino_t ino) { struct inotab *itp; for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next) if (itp->t_ino == ino) return (itp); return (NULL); } /* * Clean up and exit */ void done(int exitcode) { closemt(); if (modefile[0] != '#') { (void) truncate(modefile, 0); (void) unlink(modefile); } if (dirfile[0] != '#') { (void) truncate(dirfile, 0); (void) unlink(dirfile); } exit(exitcode); } /* * Print out information about the failure to save directory, * extended attribute, and mode information. */ static void fail_dirtmp(char *filename) { const char *tmpdir; warn("%s: cannot write directory database", filename); if (errno == ENOSPC) { if ((tmpdir = getenv("TMPDIR")) == NULL || tmpdir[0] == '\0') tmpdir = _PATH_TMP; fprintf(stderr, "Try making space in %s, %s\n%s\n", tmpdir, "or set environment variable TMPDIR", "to an alternate location with more disk space."); } done(1); } diff --git a/sbin/restore/symtab.c b/sbin/restore/symtab.c index 49ed39dd2022..0172a2c593d0 100644 --- a/sbin/restore/symtab.c +++ b/sbin/restore/symtab.c @@ -1,617 +1,615 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)symtab.c 8.3 (Berkeley) 4/28/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ /* * These routines maintain the symbol table which tracks the state * of the file system being restored. They provide lookup by either * name or inode number. They also provide for creation, deletion, * and renaming of entries. Because of the dynamic nature of pathnames, * names should not be saved, but always constructed just before they * are needed, by calling "myname". */ #include #include #include #include #include #include #include #include #include #include #include #include "restore.h" #include "extern.h" /* * The following variables define the inode symbol table. * The primary hash table is dynamically allocated based on * the number of inodes in the file system (maxino), scaled by * HASHFACTOR. The variable "entry" points to the hash table; * the variable "entrytblsize" indicates its size (in entries). */ #define HASHFACTOR 5 static struct entry **entry; static long entrytblsize; static void addino(ino_t, struct entry *); static struct entry *lookupparent(char *); static void removeentry(struct entry *); /* * Look up an entry by inode number */ struct entry * lookupino(ino_t inum) { struct entry *ep; if (inum < UFS_WINO || inum >= maxino) return (NULL); for (ep = entry[inum % entrytblsize]; ep != NULL; ep = ep->e_next) if (ep->e_ino == inum) return (ep); return (NULL); } /* * Add an entry into the entry table */ static void addino(ino_t inum, struct entry *np) { struct entry **epp; if (inum < UFS_WINO || inum >= maxino) panic("addino: out of range %ju\n", (uintmax_t)inum); epp = &entry[inum % entrytblsize]; np->e_ino = inum; np->e_next = *epp; *epp = np; if (dflag) for (np = np->e_next; np != NULL; np = np->e_next) if (np->e_ino == inum) badentry(np, "duplicate inum"); } /* * Delete an entry from the entry table */ void deleteino(ino_t inum) { struct entry *next; struct entry **prev; if (inum < UFS_WINO || inum >= maxino) panic("deleteino: out of range %ju\n", (uintmax_t)inum); prev = &entry[inum % entrytblsize]; for (next = *prev; next != NULL; next = next->e_next) { if (next->e_ino == inum) { next->e_ino = 0; *prev = next->e_next; return; } prev = &next->e_next; } panic("deleteino: %ju not found\n", (uintmax_t)inum); } /* * Look up an entry by name */ struct entry * lookupname(char *name) { struct entry *ep; char *np, *cp; char buf[MAXPATHLEN]; cp = name; for (ep = lookupino(UFS_ROOTINO); ep != NULL; ep = ep->e_entries) { for (np = buf; *cp != '/' && *cp != '\0' && np < &buf[sizeof(buf)]; ) *np++ = *cp++; if (np == &buf[sizeof(buf)]) break; *np = '\0'; for ( ; ep != NULL; ep = ep->e_sibling) if (strcmp(ep->e_name, buf) == 0) break; if (ep == NULL) break; if (*cp++ == '\0') return (ep); } return (NULL); } /* * Look up the parent of a pathname */ static struct entry * lookupparent(char *name) { struct entry *ep; char *tailindex; tailindex = strrchr(name, '/'); if (tailindex == NULL) return (NULL); *tailindex = '\0'; ep = lookupname(name); *tailindex = '/'; if (ep == NULL) return (NULL); if (ep->e_type != NODE) panic("%s is not a directory\n", name); return (ep); } /* * Determine the current pathname of a node or leaf */ char * myname(struct entry *ep) { char *cp; static char namebuf[MAXPATHLEN]; for (cp = &namebuf[MAXPATHLEN - 2]; cp > &namebuf[ep->e_namlen]; ) { cp -= ep->e_namlen; memmove(cp, ep->e_name, (long)ep->e_namlen); if (ep == lookupino(UFS_ROOTINO)) return (cp); *(--cp) = '/'; ep = ep->e_parent; } panic("%s: pathname too long\n", cp); return(cp); } /* * Unused symbol table entries are linked together on a free list * headed by the following pointer. */ static struct entry *freelist = NULL; /* * add an entry to the symbol table */ struct entry * addentry(char *name, ino_t inum, int type) { struct entry *np, *ep; if (freelist != NULL) { np = freelist; freelist = np->e_next; memset(np, 0, (long)sizeof(struct entry)); } else { np = (struct entry *)calloc(1, sizeof(struct entry)); if (np == NULL) panic("no memory to extend symbol table\n"); } np->e_type = type & ~LINK; ep = lookupparent(name); if (ep == NULL) { if (inum != UFS_ROOTINO || lookupino(UFS_ROOTINO) != NULL) panic("bad name to addentry %s\n", name); np->e_name = savename(name); np->e_namlen = strlen(name); np->e_parent = np; addino(UFS_ROOTINO, np); return (np); } np->e_name = savename(strrchr(name, '/') + 1); np->e_namlen = strlen(np->e_name); np->e_parent = ep; np->e_sibling = ep->e_entries; ep->e_entries = np; if (type & LINK) { ep = lookupino(inum); if (ep == NULL) panic("link to non-existent name\n"); np->e_ino = inum; np->e_links = ep->e_links; ep->e_links = np; } else if (inum != 0) { if (lookupino(inum) != NULL) panic("duplicate entry\n"); addino(inum, np); } return (np); } /* * delete an entry from the symbol table */ void freeentry(struct entry *ep) { struct entry *np; ino_t inum; if (ep->e_flags != REMOVED) badentry(ep, "not marked REMOVED"); if (ep->e_type == NODE) { if (ep->e_links != NULL) badentry(ep, "freeing referenced directory"); if (ep->e_entries != NULL) badentry(ep, "freeing non-empty directory"); } if (ep->e_ino != 0) { np = lookupino(ep->e_ino); if (np == NULL) badentry(ep, "lookupino failed"); if (np == ep) { inum = ep->e_ino; deleteino(inum); if (ep->e_links != NULL) addino(inum, ep->e_links); } else { for (; np != NULL; np = np->e_links) { if (np->e_links == ep) { np->e_links = ep->e_links; break; } } if (np == NULL) badentry(ep, "link not found"); } } removeentry(ep); freename(ep->e_name); ep->e_next = freelist; freelist = ep; } /* * Relocate an entry in the tree structure */ void moveentry(struct entry *ep, char *newname) { struct entry *np; char *cp; np = lookupparent(newname); if (np == NULL) badentry(ep, "cannot move ROOT"); if (np != ep->e_parent) { removeentry(ep); ep->e_parent = np; ep->e_sibling = np->e_entries; np->e_entries = ep; } cp = strrchr(newname, '/') + 1; freename(ep->e_name); ep->e_name = savename(cp); ep->e_namlen = strlen(cp); if (strcmp(gentempname(ep), ep->e_name) == 0) ep->e_flags |= TMPNAME; else ep->e_flags &= ~TMPNAME; } /* * Remove an entry in the tree structure */ static void removeentry(struct entry *ep) { struct entry *np; np = ep->e_parent; if (np->e_entries == ep) { np->e_entries = ep->e_sibling; } else { for (np = np->e_entries; np != NULL; np = np->e_sibling) { if (np->e_sibling == ep) { np->e_sibling = ep->e_sibling; break; } } if (np == NULL) badentry(ep, "cannot find entry in parent list"); } } /* * Table of unused string entries, sorted by length. * * Entries are allocated in STRTBLINCR sized pieces so that names * of similar lengths can use the same entry. The value of STRTBLINCR * is chosen so that every entry has at least enough space to hold * a "struct strtbl" header. Thus every entry can be linked onto an * appropriate free list. * * NB. The macro "allocsize" below assumes that "struct strhdr" * has a size that is a power of two. */ struct strhdr { struct strhdr *next; }; #define STRTBLINCR (sizeof(struct strhdr)) #define allocsize(size) roundup2((size) + 1, STRTBLINCR) static struct strhdr strtblhdr[allocsize(NAME_MAX) / STRTBLINCR]; /* * Allocate space for a name. It first looks to see if it already * has an appropriate sized entry, and if not allocates a new one. */ char * savename(char *name) { struct strhdr *np; size_t len; char *cp; if (name == NULL) panic("bad name\n"); len = strlen(name); np = strtblhdr[len / STRTBLINCR].next; if (np != NULL) { strtblhdr[len / STRTBLINCR].next = np->next; cp = (char *)np; } else { cp = malloc(allocsize(len)); if (cp == NULL) panic("no space for string table\n"); } (void) strcpy(cp, name); return (cp); } /* * Free space for a name. The resulting entry is linked onto the * appropriate free list. */ void freename(char *name) { struct strhdr *tp, *np; tp = &strtblhdr[strlen(name) / STRTBLINCR]; np = (struct strhdr *)name; np->next = tp->next; tp->next = np; } /* * Useful quantities placed at the end of a dumped symbol table. */ struct symtableheader { int32_t volno; int32_t stringsize; int32_t entrytblsize; time_t dumptime; time_t dumpdate; ino_t maxino; int32_t ntrec; }; /* * dump a snapshot of the symbol table */ void dumpsymtable(char *filename, long checkpt) { struct entry *ep, *tep; ino_t i; struct entry temp, *tentry; long mynum = 1, stroff = 0; FILE *fd; struct symtableheader hdr; vprintf(stdout, "Checkpointing the restore\n"); if (Nflag) return; if ((fd = fopen(filename, "w")) == NULL) { fprintf(stderr, "fopen: %s\n", strerror(errno)); panic("cannot create save file %s for symbol table\n", filename); done(1); } clearerr(fd); /* * Assign indices to each entry * Write out the string entries */ for (i = UFS_WINO; i <= maxino; i++) { for (ep = lookupino(i); ep != NULL; ep = ep->e_links) { ep->e_index = mynum++; (void) fwrite(ep->e_name, sizeof(char), (int)allocsize(ep->e_namlen), fd); } } /* * Convert pointers to indexes, and output */ tep = &temp; stroff = 0; for (i = UFS_WINO; i <= maxino; i++) { for (ep = lookupino(i); ep != NULL; ep = ep->e_links) { memmove(tep, ep, (long)sizeof(struct entry)); tep->e_name = (char *)stroff; stroff += allocsize(ep->e_namlen); tep->e_parent = (struct entry *)ep->e_parent->e_index; if (ep->e_links != NULL) tep->e_links = (struct entry *)ep->e_links->e_index; if (ep->e_sibling != NULL) tep->e_sibling = (struct entry *)ep->e_sibling->e_index; if (ep->e_entries != NULL) tep->e_entries = (struct entry *)ep->e_entries->e_index; if (ep->e_next != NULL) tep->e_next = (struct entry *)ep->e_next->e_index; (void) fwrite((char *)tep, sizeof(struct entry), 1, fd); } } /* * Convert entry pointers to indexes, and output */ for (i = 0; i < entrytblsize; i++) { if (entry[i] == NULL) tentry = NULL; else tentry = (struct entry *)entry[i]->e_index; (void) fwrite((char *)&tentry, sizeof(struct entry *), 1, fd); } hdr.volno = checkpt; hdr.maxino = maxino; hdr.entrytblsize = entrytblsize; hdr.stringsize = stroff; hdr.dumptime = dumptime; hdr.dumpdate = dumpdate; hdr.ntrec = ntrec; (void) fwrite((char *)&hdr, sizeof(struct symtableheader), 1, fd); if (ferror(fd)) { fprintf(stderr, "fwrite: %s\n", strerror(errno)); panic("output error to file %s writing symbol table\n", filename); } (void) fclose(fd); } /* * Initialize a symbol table from a file */ void initsymtable(char *filename) { char *base; long tblsize; struct entry *ep; struct entry *baseep, *lep; struct symtableheader hdr; struct stat stbuf; long i; int fd; vprintf(stdout, "Initialize symbol table.\n"); if (filename == NULL) { entrytblsize = maxino / HASHFACTOR; entry = calloc((unsigned)entrytblsize, sizeof(struct entry *)); if (entry == NULL) panic("no memory for entry table\n"); ep = addentry(".", UFS_ROOTINO, NODE); ep->e_flags |= NEW; return; } if ((fd = open(filename, O_RDONLY, 0)) < 0) { fprintf(stderr, "open: %s\n", strerror(errno)); panic("cannot open symbol table file %s\n", filename); } if (fstat(fd, &stbuf) < 0) { fprintf(stderr, "stat: %s\n", strerror(errno)); panic("cannot stat symbol table file %s\n", filename); } tblsize = stbuf.st_size - sizeof(struct symtableheader); base = calloc(sizeof(char), (unsigned)tblsize); if (base == NULL) panic("cannot allocate space for symbol table\n"); if (read(fd, base, (int)tblsize) < 0 || read(fd, (char *)&hdr, sizeof(struct symtableheader)) < 0) { fprintf(stderr, "read: %s\n", strerror(errno)); panic("cannot read symbol table file %s\n", filename); } (void)close(fd); switch (command) { case 'r': /* * For normal continuation, insure that we are using * the next incremental tape */ if (hdr.dumpdate != dumptime) { if (hdr.dumpdate < dumptime) fprintf(stderr, "Incremental tape too low\n"); else fprintf(stderr, "Incremental tape too high\n"); done(1); } break; case 'R': /* * For restart, insure that we are using the same tape */ curfile.action = SKIP; dumptime = hdr.dumptime; dumpdate = hdr.dumpdate; if (!bflag) newtapebuf(hdr.ntrec); getvol(hdr.volno); break; default: panic("initsymtable called from command %c\n", command); break; } maxino = hdr.maxino; entrytblsize = hdr.entrytblsize; entry = (struct entry **) (base + tblsize - (entrytblsize * sizeof(struct entry *))); baseep = (struct entry *)(base + hdr.stringsize - sizeof(struct entry)); lep = (struct entry *)entry; for (i = 0; i < entrytblsize; i++) { if (entry[i] == NULL) continue; entry[i] = &baseep[(long)entry[i]]; } for (ep = &baseep[1]; ep < lep; ep++) { ep->e_name = base + (long)ep->e_name; ep->e_parent = &baseep[(long)ep->e_parent]; if (ep->e_sibling != NULL) ep->e_sibling = &baseep[(long)ep->e_sibling]; if (ep->e_links != NULL) ep->e_links = &baseep[(long)ep->e_links]; if (ep->e_entries != NULL) ep->e_entries = &baseep[(long)ep->e_entries]; if (ep->e_next != NULL) ep->e_next = &baseep[(long)ep->e_next]; } } diff --git a/sbin/restore/utilities.c b/sbin/restore/utilities.c index a53d42f9e600..d95330f6de4a 100644 --- a/sbin/restore/utilities.c +++ b/sbin/restore/utilities.c @@ -1,424 +1,422 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)utilities.c 8.5 (Berkeley) 4/28/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include "restore.h" #include "extern.h" /* * Insure that all the components of a pathname exist. */ void pathcheck(char *name) { char *cp; struct entry *ep; char *start; start = strchr(name, '/'); if (start == NULL) return; for (cp = start; *cp != '\0'; cp++) { if (*cp != '/') continue; *cp = '\0'; ep = lookupname(name); if (ep == NULL) { /* Safe; we know the pathname exists in the dump. */ ep = addentry(name, pathsearch(name)->d_ino, NODE); newnode(ep); } ep->e_flags |= NEW|KEEP; *cp = '/'; } } /* * Change a name to a unique temporary name. */ void mktempname(struct entry *ep) { char oldname[MAXPATHLEN]; if (ep->e_flags & TMPNAME) badentry(ep, "mktempname: called with TMPNAME"); ep->e_flags |= TMPNAME; (void) strcpy(oldname, myname(ep)); freename(ep->e_name); ep->e_name = savename(gentempname(ep)); ep->e_namlen = strlen(ep->e_name); renameit(oldname, myname(ep)); } /* * Generate a temporary name for an entry. */ char * gentempname(struct entry *ep) { static char name[MAXPATHLEN]; struct entry *np; long i = 0; for (np = lookupino(ep->e_ino); np != NULL && np != ep; np = np->e_links) i++; if (np == NULL) badentry(ep, "not on ino list"); (void) sprintf(name, "%s%ld%lu", TMPHDR, i, (u_long)ep->e_ino); return (name); } /* * Rename a file or directory. */ void renameit(char *from, char *to) { if (!Nflag && rename(from, to) < 0) { fprintf(stderr, "warning: cannot rename %s to %s: %s\n", from, to, strerror(errno)); return; } vprintf(stdout, "rename %s to %s\n", from, to); } /* * Create a new node (directory). */ void newnode(struct entry *np) { char *cp; if (np->e_type != NODE) badentry(np, "newnode: not a node"); cp = myname(np); if (!Nflag && mkdir(cp, 0777) < 0 && !uflag) { np->e_flags |= EXISTED; fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno)); return; } vprintf(stdout, "Make node %s\n", cp); } /* * Remove an old node (directory). */ void removenode(struct entry *ep) { char *cp; if (ep->e_type != NODE) badentry(ep, "removenode: not a node"); if (ep->e_entries != NULL) badentry(ep, "removenode: non-empty directory"); ep->e_flags |= REMOVED; ep->e_flags &= ~TMPNAME; cp = myname(ep); if (!Nflag && rmdir(cp) < 0) { fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno)); return; } vprintf(stdout, "Remove node %s\n", cp); } /* * Remove a leaf. */ void removeleaf(struct entry *ep) { char *cp; if (ep->e_type != LEAF) badentry(ep, "removeleaf: not a leaf"); ep->e_flags |= REMOVED; ep->e_flags &= ~TMPNAME; cp = myname(ep); if (!Nflag && unlink(cp) < 0) { fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno)); return; } vprintf(stdout, "Remove leaf %s\n", cp); } /* * Create a link. */ int linkit(char *existing, char *new, int type) { /* if we want to unlink first, do it now so *link() won't fail */ if (uflag && !Nflag) (void)unlink(new); if (type == SYMLINK) { if (!Nflag && symlink(existing, new) < 0) { fprintf(stderr, "warning: cannot create symbolic link %s->%s: %s\n", new, existing, strerror(errno)); return (FAIL); } } else if (type == HARDLINK) { int ret; if (!Nflag && (ret = link(existing, new)) < 0) { struct stat s; /* * Most likely, the schg flag is set. Clear the * flags and try again. */ if (stat(existing, &s) == 0 && s.st_flags != 0 && chflags(existing, 0) == 0) { ret = link(existing, new); chflags(existing, s.st_flags); } if (ret < 0) { fprintf(stderr, "warning: cannot create " "hard link %s->%s: %s\n", new, existing, strerror(errno)); return (FAIL); } } } else { panic("linkit: unknown type %d\n", type); return (FAIL); } vprintf(stdout, "Create %s link %s->%s\n", type == SYMLINK ? "symbolic" : "hard", new, existing); return (GOOD); } /* * Create a whiteout. */ int addwhiteout(char *name) { if (!Nflag && mknod(name, S_IFWHT, 0) < 0) { fprintf(stderr, "warning: cannot create whiteout %s: %s\n", name, strerror(errno)); return (FAIL); } vprintf(stdout, "Create whiteout %s\n", name); return (GOOD); } /* * Delete a whiteout. */ void delwhiteout(struct entry *ep) { char *name; if (ep->e_type != LEAF) badentry(ep, "delwhiteout: not a leaf"); ep->e_flags |= REMOVED; ep->e_flags &= ~TMPNAME; name = myname(ep); if (!Nflag && undelete(name) < 0) { fprintf(stderr, "warning: cannot delete whiteout %s: %s\n", name, strerror(errno)); return; } vprintf(stdout, "Delete whiteout %s\n", name); } /* * find lowest number file (above "start") that needs to be extracted */ ino_t lowerbnd(ino_t start) { struct entry *ep; for ( ; start < maxino; start++) { ep = lookupino(start); if (ep == NULL || ep->e_type == NODE) continue; if (ep->e_flags & (NEW|EXTRACT)) return (start); } return (start); } /* * find highest number file (below "start") that needs to be extracted */ ino_t upperbnd(ino_t start) { struct entry *ep; for ( ; start > UFS_ROOTINO; start--) { ep = lookupino(start); if (ep == NULL || ep->e_type == NODE) continue; if (ep->e_flags & (NEW|EXTRACT)) return (start); } return (start); } /* * report on a badly formed entry */ void badentry(struct entry *ep, char *msg) { fprintf(stderr, "bad entry: %s\n", msg); fprintf(stderr, "name: %s\n", myname(ep)); fprintf(stderr, "parent name %s\n", myname(ep->e_parent)); if (ep->e_sibling != NULL) fprintf(stderr, "sibling name: %s\n", myname(ep->e_sibling)); if (ep->e_entries != NULL) fprintf(stderr, "next entry name: %s\n", myname(ep->e_entries)); if (ep->e_links != NULL) fprintf(stderr, "next link name: %s\n", myname(ep->e_links)); if (ep->e_next != NULL) fprintf(stderr, "next hashchain name: %s\n", myname(ep->e_next)); fprintf(stderr, "entry type: %s\n", ep->e_type == NODE ? "NODE" : "LEAF"); fprintf(stderr, "inode number: %lu\n", (u_long)ep->e_ino); panic("flags: %s\n", flagvalues(ep)); } /* * Construct a string indicating the active flag bits of an entry. */ char * flagvalues(struct entry *ep) { static char flagbuf[BUFSIZ]; (void) strcpy(flagbuf, "|NIL"); flagbuf[0] = '\0'; if (ep->e_flags & REMOVED) (void) strcat(flagbuf, "|REMOVED"); if (ep->e_flags & TMPNAME) (void) strcat(flagbuf, "|TMPNAME"); if (ep->e_flags & EXTRACT) (void) strcat(flagbuf, "|EXTRACT"); if (ep->e_flags & NEW) (void) strcat(flagbuf, "|NEW"); if (ep->e_flags & KEEP) (void) strcat(flagbuf, "|KEEP"); if (ep->e_flags & EXISTED) (void) strcat(flagbuf, "|EXISTED"); return (&flagbuf[1]); } /* * Check to see if a name is on a dump tape. */ ino_t dirlookup(const char *name) { struct direct *dp; ino_t ino; ino = ((dp = pathsearch(name)) == NULL) ? 0 : dp->d_ino; if (ino == 0 || TSTINO(ino, dumpmap) == 0) fprintf(stderr, "%s is not on the tape\n", name); return (ino); } /* * Elicit a reply. */ int reply(char *question) { int c; do { fprintf(stderr, "%s? [yn] ", question); (void) fflush(stderr); c = getc(terminal); while (c != '\n' && getc(terminal) != '\n') if (c == EOF) return (FAIL); } while (c != 'y' && c != 'n'); if (c == 'y') return (GOOD); return (FAIL); } /* * handle unexpected inconsistencies */ #include void panic(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); if (yflag) return; if (reply("abort") == GOOD) { if (reply("dump core") == GOOD) abort(); done(1); } } diff --git a/sbin/umount/umount.c b/sbin/umount/umount.c index 9119c5be0da1..aca7c201bc9b 100644 --- a/sbin/umount/umount.c +++ b/sbin/umount/umount.c @@ -1,657 +1,655 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1980, 1989, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)umount.c 8.8 (Berkeley) 5/8/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mounttab.h" typedef enum { FIND, REMOVE, CHECKUNIQUE } dowhat; static struct addrinfo *nfshost_ai = NULL; static int fflag, vflag; static char *nfshost; struct statfs *checkmntlist(char *); int checkvfsname (const char *, char **); struct statfs *getmntentry(const char *fromname, const char *onname, fsid_t *fsid, dowhat what); char **makevfslist (const char *); size_t mntinfo (struct statfs **); int namematch (struct addrinfo *); int parsehexfsid(const char *hex, fsid_t *fsid); int sacmp (void *, void *); int umountall (char **); int checkname (char *, char **); int umountfs(struct statfs *sfs); void usage (void); int xdr_dir (XDR *, char *); int main(int argc, char *argv[]) { int all, errs, ch, mntsize, error, nfsforce, ret; char **typelist = NULL; struct statfs *mntbuf, *sfs; struct addrinfo hints; nfsforce = all = errs = 0; while ((ch = getopt(argc, argv, "AaF:fh:Nnt:v")) != -1) switch (ch) { case 'A': all = 2; break; case 'a': all = 1; break; case 'F': setfstab(optarg); break; case 'f': fflag |= MNT_FORCE; break; case 'h': /* -h implies -A. */ all = 2; nfshost = optarg; break; case 'N': nfsforce = 1; break; case 'n': fflag |= MNT_NONBUSY; break; case 't': if (typelist != NULL) err(1, "only one -t option may be specified"); typelist = makevfslist(optarg); break; case 'v': vflag = 1; break; default: usage(); /* NOTREACHED */ } argc -= optind; argv += optind; if ((fflag & MNT_FORCE) != 0 && (fflag & MNT_NONBUSY) != 0) err(1, "-f and -n are mutually exclusive"); if ((argc == 0 && !all) || (argc != 0 && all)) usage(); if (nfsforce != 0 && (argc == 0 || nfshost != NULL || typelist != NULL)) usage(); /* -h implies "-t nfs" if no -t flag. */ if ((nfshost != NULL) && (typelist == NULL)) typelist = makevfslist("nfs"); if (nfshost != NULL) { memset(&hints, 0, sizeof hints); error = getaddrinfo(nfshost, NULL, &hints, &nfshost_ai); if (error) errx(1, "%s: %s", nfshost, gai_strerror(error)); } switch (all) { case 2: if ((mntsize = mntinfo(&mntbuf)) <= 0) break; /* * We unmount the nfs-mounts in the reverse order * that they were mounted. */ for (errs = 0, mntsize--; mntsize > 0; mntsize--) { sfs = &mntbuf[mntsize]; if (checkvfsname(sfs->f_fstypename, typelist)) continue; if (strcmp(sfs->f_mntonname, "/dev") == 0) continue; if (umountfs(sfs) != 0) errs = 1; } free(mntbuf); break; case 1: if (setfsent() == 0) err(1, "%s", getfstab()); errs = umountall(typelist); break; case 0: for (errs = 0; *argv != NULL; ++argv) if (nfsforce != 0) { /* * First do the nfssvc() syscall to shut down * the mount point and then do the forced * dismount. */ ret = nfssvc(NFSSVC_FORCEDISM, *argv); if (ret >= 0) ret = unmount(*argv, MNT_FORCE); if (ret < 0) { warn("%s", *argv); errs = 1; } } else if (checkname(*argv, typelist) != 0) errs = 1; break; } exit(errs); } int umountall(char **typelist) { struct xvfsconf vfc; struct fstab *fs; int rval; char *cp; static int firstcall = 1; if ((fs = getfsent()) != NULL) firstcall = 0; else if (firstcall) errx(1, "fstab reading failure"); else return (0); do { /* Ignore the root. */ if (strcmp(fs->fs_file, "/") == 0) continue; /* * !!! * Historic practice: ignore unknown FSTAB_* fields. */ if (strcmp(fs->fs_type, FSTAB_RW) && strcmp(fs->fs_type, FSTAB_RO) && strcmp(fs->fs_type, FSTAB_RQ)) continue; /* Ignore unknown file system types. */ if (getvfsbyname(fs->fs_vfstype, &vfc) == -1) continue; if (checkvfsname(fs->fs_vfstype, typelist)) continue; /* * We want to unmount the file systems in the reverse order * that they were mounted. So, we save off the file name * in some allocated memory, and then call recursively. */ if ((cp = malloc((size_t)strlen(fs->fs_file) + 1)) == NULL) err(1, "malloc failed"); (void)strcpy(cp, fs->fs_file); rval = umountall(typelist); rval = checkname(cp, typelist) || rval; free(cp); return (rval); } while ((fs = getfsent()) != NULL); return (0); } /* * Do magic checks on mountpoint/device/fsid, and then call unmount(2). */ int checkname(char *mntname, char **typelist) { char buf[MAXPATHLEN]; struct statfs sfsbuf; struct stat sb; struct statfs *sfs; char *delimp; dev_t dev; int len; /* * 1. Check if the name exists in the mounttable. */ sfs = checkmntlist(mntname); /* * 2. Remove trailing slashes if there are any. After that * we look up the name in the mounttable again. */ if (sfs == NULL) { len = strlen(mntname); while (len > 1 && mntname[len - 1] == '/') mntname[--len] = '\0'; sfs = checkmntlist(mntname); } /* * 3. Check if the deprecated NFS syntax with an '@' has been used * and translate it to the ':' syntax. Look up the name in the * mount table again. */ if (sfs == NULL && (delimp = strrchr(mntname, '@')) != NULL) { snprintf(buf, sizeof(buf), "%s:%.*s", delimp + 1, (int)(delimp - mntname), mntname); len = strlen(buf); while (len > 1 && buf[len - 1] == '/') buf[--len] = '\0'; sfs = checkmntlist(buf); } /* * 4. Resort to a statfs(2) call. This is the last check so that * hung NFS filesystems for example can be unmounted without * potentially blocking forever in statfs() as long as the * filesystem is specified unambiguously. This covers all the * hard cases such as symlinks and mismatches between the * mount list and reality. * We also do this if an ambiguous mount point was specified. */ if (sfs == NULL || (getmntentry(NULL, mntname, NULL, FIND) != NULL && getmntentry(NULL, mntname, NULL, CHECKUNIQUE) == NULL)) { if (statfs(mntname, &sfsbuf) != 0) { warn("%s: statfs", mntname); } else if (stat(mntname, &sb) != 0) { warn("%s: stat", mntname); } else if (S_ISDIR(sb.st_mode)) { /* Check that `mntname' is the root directory. */ dev = sb.st_dev; snprintf(buf, sizeof(buf), "%s/..", mntname); if (stat(buf, &sb) != 0) { warn("%s: stat", buf); } else if (sb.st_dev == dev) { warnx("%s: not a file system root directory", mntname); return (1); } else sfs = &sfsbuf; } } if (sfs == NULL) { warnx("%s: unknown file system", mntname); return (1); } if (checkvfsname(sfs->f_fstypename, typelist)) return (1); return (umountfs(sfs)); } /* * NFS stuff and unmount(2) call */ int umountfs(struct statfs *sfs) { char fsidbuf[64]; enum clnt_stat clnt_stat; struct timeval try; struct addrinfo *ai, hints; int do_rpc; CLIENT *clp; char *nfsdirname, *orignfsdirname; char *hostp, *delimp; char buf[1024]; struct nfscl_dumpmntopts dumpmntopts; const char *proto_ptr = NULL; ai = NULL; do_rpc = 0; hostp = NULL; nfsdirname = delimp = orignfsdirname = NULL; memset(&hints, 0, sizeof hints); if (strcmp(sfs->f_fstypename, "nfs") == 0) { if ((nfsdirname = strdup(sfs->f_mntfromname)) == NULL) err(1, "strdup"); orignfsdirname = nfsdirname; if (*nfsdirname == '[' && (delimp = strchr(nfsdirname + 1, ']')) != NULL && *(delimp + 1) == ':') { hostp = nfsdirname + 1; nfsdirname = delimp + 2; } else if ((delimp = strrchr(nfsdirname, ':')) != NULL) { hostp = nfsdirname; nfsdirname = delimp + 1; } if (hostp != NULL) { *delimp = '\0'; getaddrinfo(hostp, NULL, &hints, &ai); if (ai == NULL) { warnx("can't get net id for host"); } } /* * Check if we have to start the rpc-call later. * If there are still identical nfs-names mounted, * we skip the rpc-call. Obviously this has to * happen before unmount(2), but it should happen * after the previous namecheck. * A non-NULL return means that this is the last * mount from mntfromname that is still mounted. */ if (getmntentry(sfs->f_mntfromname, NULL, NULL, CHECKUNIQUE) != NULL) { do_rpc = 1; proto_ptr = "udp"; /* * Try and find out whether this NFS mount is NFSv4 and * what protocol is being used. If this fails, the * default is NFSv2,3 and use UDP for the Unmount RPC. */ dumpmntopts.ndmnt_fname = sfs->f_mntonname; dumpmntopts.ndmnt_buf = buf; dumpmntopts.ndmnt_blen = sizeof(buf); if (nfssvc(NFSSVC_DUMPMNTOPTS, &dumpmntopts) >= 0) { if (strstr(buf, "nfsv4,") != NULL) do_rpc = 0; else if (strstr(buf, ",tcp,") != NULL) proto_ptr = "tcp"; } } } if (!namematch(ai)) { free(orignfsdirname); return (1); } /* First try to unmount using the file system ID. */ snprintf(fsidbuf, sizeof(fsidbuf), "FSID:%d:%d", sfs->f_fsid.val[0], sfs->f_fsid.val[1]); if (unmount(fsidbuf, fflag | MNT_BYFSID) != 0) { /* XXX, non-root users get a zero fsid, so don't warn. */ if (errno != ENOENT || sfs->f_fsid.val[0] != 0 || sfs->f_fsid.val[1] != 0) warn("unmount of %s failed", sfs->f_mntonname); if (errno != ENOENT) { free(orignfsdirname); return (1); } /* Compatibility for old kernels. */ if (sfs->f_fsid.val[0] != 0 || sfs->f_fsid.val[1] != 0) warnx("retrying using path instead of file system ID"); if (unmount(sfs->f_mntonname, fflag) != 0) { warn("unmount of %s failed", sfs->f_mntonname); free(orignfsdirname); return (1); } } /* Mark this file system as unmounted. */ getmntentry(NULL, NULL, &sfs->f_fsid, REMOVE); if (vflag) (void)printf("%s: unmount from %s\n", sfs->f_mntfromname, sfs->f_mntonname); /* * Report to mountd-server which nfsname * has been unmounted. */ if (ai != NULL && !(fflag & MNT_FORCE) && do_rpc) { clp = clnt_create(hostp, MOUNTPROG, MOUNTVERS3, proto_ptr); if (clp == NULL) { warnx("%s: %s", hostp, clnt_spcreateerror("MOUNTPROG")); free(orignfsdirname); return (1); } clp->cl_auth = authsys_create_default(); try.tv_sec = 20; try.tv_usec = 0; clnt_stat = clnt_call(clp, MOUNTPROC_UMNT, (xdrproc_t)xdr_dir, nfsdirname, (xdrproc_t)xdr_void, (caddr_t)0, try); if (clnt_stat != RPC_SUCCESS) { warnx("%s: %s", hostp, clnt_sperror(clp, "RPCMNT_UMOUNT")); free(orignfsdirname); return (1); } /* * Remove the unmounted entry from /var/db/mounttab. */ if (read_mtab()) { clean_mtab(hostp, nfsdirname, vflag); if(!write_mtab(vflag)) warnx("cannot remove mounttab entry %s:%s", hostp, nfsdirname); free_mtab(); } auth_destroy(clp->cl_auth); clnt_destroy(clp); } free(orignfsdirname); return (0); } struct statfs * getmntentry(const char *fromname, const char *onname, fsid_t *fsid, dowhat what) { static struct statfs *mntbuf; static size_t mntsize = 0; static int *mntcheck = NULL; struct statfs *sfs, *foundsfs; int i, count; if (mntsize <= 0) { if ((mntsize = mntinfo(&mntbuf)) <= 0) return (NULL); } if (mntcheck == NULL) { if ((mntcheck = calloc(mntsize + 1, sizeof(int))) == NULL) err(1, "calloc"); } /* * We want to get the file systems in the reverse order * that they were mounted. Unmounted file systems are marked * in a table called 'mntcheck'. */ count = 0; foundsfs = NULL; for (i = mntsize - 1; i >= 0; i--) { if (mntcheck[i]) continue; sfs = &mntbuf[i]; if (fromname != NULL && strcmp(sfs->f_mntfromname, fromname) != 0) continue; if (onname != NULL && strcmp(sfs->f_mntonname, onname) != 0) continue; if (fsid != NULL && fsidcmp(&sfs->f_fsid, fsid) != 0) continue; switch (what) { case CHECKUNIQUE: foundsfs = sfs; count++; continue; case REMOVE: mntcheck[i] = 1; break; default: break; } return (sfs); } if (what == CHECKUNIQUE && count == 1) return (foundsfs); return (NULL); } int sacmp(void *sa1, void *sa2) { void *p1, *p2; int len; if (((struct sockaddr *)sa1)->sa_family != ((struct sockaddr *)sa2)->sa_family) return (1); switch (((struct sockaddr *)sa1)->sa_family) { case AF_INET: p1 = &((struct sockaddr_in *)sa1)->sin_addr; p2 = &((struct sockaddr_in *)sa2)->sin_addr; len = 4; break; case AF_INET6: p1 = &((struct sockaddr_in6 *)sa1)->sin6_addr; p2 = &((struct sockaddr_in6 *)sa2)->sin6_addr; len = 16; if (((struct sockaddr_in6 *)sa1)->sin6_scope_id != ((struct sockaddr_in6 *)sa2)->sin6_scope_id) return (1); break; default: return (1); } return memcmp(p1, p2, len); } int namematch(struct addrinfo *ai) { struct addrinfo *aip; if (nfshost == NULL || nfshost_ai == NULL) return (1); while (ai != NULL) { aip = nfshost_ai; while (aip != NULL) { if (sacmp(ai->ai_addr, aip->ai_addr) == 0) return (1); aip = aip->ai_next; } ai = ai->ai_next; } return (0); } struct statfs * checkmntlist(char *mntname) { struct statfs *sfs; fsid_t fsid; sfs = NULL; if (parsehexfsid(mntname, &fsid) == 0) sfs = getmntentry(NULL, NULL, &fsid, FIND); if (sfs == NULL) sfs = getmntentry(NULL, mntname, NULL, FIND); if (sfs == NULL) sfs = getmntentry(mntname, NULL, NULL, FIND); return (sfs); } size_t mntinfo(struct statfs **mntbuf) { static struct statfs *origbuf; size_t bufsize; int mntsize; mntsize = getfsstat(NULL, 0, MNT_NOWAIT); if (mntsize <= 0) return (0); bufsize = (mntsize + 1) * sizeof(struct statfs); if ((origbuf = malloc(bufsize)) == NULL) err(1, "malloc"); mntsize = getfsstat(origbuf, (long)bufsize, MNT_NOWAIT); *mntbuf = origbuf; return (mntsize); } /* * Convert a hexadecimal filesystem ID to an fsid_t. * Returns 0 on success. */ int parsehexfsid(const char *hex, fsid_t *fsid) { char hexbuf[3]; int i; if (strlen(hex) != sizeof(*fsid) * 2) return (-1); hexbuf[2] = '\0'; for (i = 0; i < (int)sizeof(*fsid); i++) { hexbuf[0] = hex[i * 2]; hexbuf[1] = hex[i * 2 + 1]; if (!isxdigit(hexbuf[0]) || !isxdigit(hexbuf[1])) return (-1); ((u_char *)fsid)[i] = strtol(hexbuf, NULL, 16); } return (0); } /* * xdr routines for mount rpc's */ int xdr_dir(XDR *xdrsp, char *dirp) { return (xdr_string(xdrsp, &dirp, MNTPATHLEN)); } void usage(void) { (void)fprintf(stderr, "%s\n%s\n", "usage: umount [-fNnv] special ... | node ... | fsid ...", " umount -a | -A [-F fstab] [-fnv] [-h host] [-t type]"); exit(1); } diff --git a/stand/libsa/powerpc/syncicache.c b/stand/libsa/powerpc/syncicache.c index 434dcec63416..0fcb1914a0da 100644 --- a/stand/libsa/powerpc/syncicache.c +++ b/stand/libsa/powerpc/syncicache.c @@ -1,103 +1,98 @@ /*- * Copyright (C) 1995-1997, 1999 Wolfgang Solfrank. * Copyright (C) 1995-1997, 1999 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $NetBSD: syncicache.c,v 1.2 1999/05/05 12:36:40 tsubai Exp $ */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #if defined(_KERNEL) || defined(_STANDALONE) #include #include #include #endif #include #include #include #ifdef _STANDALONE int cacheline_size = 32; #endif #if !defined(_KERNEL) && !defined(_STANDALONE) #include int cacheline_size = 0; static void getcachelinesize(void); static void getcachelinesize() { static int cachemib[] = { CTL_MACHDEP, CPU_CACHELINE }; int clen; clen = sizeof(cacheline_size); if (sysctl(cachemib, sizeof(cachemib) / sizeof(cachemib[0]), &cacheline_size, &clen, NULL, 0) < 0 || !cacheline_size) { abort(); } } #endif void __syncicache(void *from, int len) { int l, off; char *p; #if !defined(_KERNEL) && !defined(_STANDALONE) if (!cacheline_size) getcachelinesize(); #endif off = (u_int)from & (cacheline_size - 1); l = len += off; p = (char *)from - off; do { __asm __volatile ("dcbst 0,%0" :: "r"(p)); p += cacheline_size; } while ((l -= cacheline_size) > 0); __asm __volatile ("sync"); p = (char *)from - off; do { __asm __volatile ("icbi 0,%0" :: "r"(p)); p += cacheline_size; } while ((len -= cacheline_size) > 0); __asm __volatile ("sync; isync"); } diff --git a/sys/netpfil/ipfilter/netinet/fil.c b/sys/netpfil/ipfilter/netinet/fil.c index b04ec3496a65..76fde8622498 100644 --- a/sys/netpfil/ipfilter/netinet/fil.c +++ b/sys/netpfil/ipfilter/netinet/fil.c @@ -1,9959 +1,9958 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * Copyright 2008 Sun Microsystems. * * $Id$ * */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #if defined(_KERNEL) && defined(__FreeBSD__) # if !defined(IPFILTER_LKM) # include "opt_inet6.h" # endif # include #else # include #endif #if defined(__SVR4) || defined(sun) /* SOLARIS */ # include #endif # include #if defined(_KERNEL) # include # include #else # include # include # include # include # include # define _KERNEL # include # undef _KERNEL #endif #if !defined(__SVR4) # include #else # include # if (SOLARIS2 < 5) && defined(sun) # include # endif #endif # include #include #include #ifdef sun # include #endif #include #include #include #include # include # include #include "netinet/ip_compat.h" #ifdef USE_INET6 # include # if !SOLARIS && defined(_KERNEL) # include # endif #endif #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_frag.h" #include "netinet/ip_state.h" #include "netinet/ip_proxy.h" #include "netinet/ip_auth.h" #ifdef IPFILTER_SCAN # include "netinet/ip_scan.h" #endif #include "netinet/ip_sync.h" #include "netinet/ip_lookup.h" #include "netinet/ip_pool.h" #include "netinet/ip_htable.h" #ifdef IPFILTER_COMPILED # include "netinet/ip_rules.h" #endif #if defined(IPFILTER_BPF) && defined(_KERNEL) # include #endif #if defined(__FreeBSD__) # include #endif #include "netinet/ipl.h" #if defined(__NetBSD__) && (__NetBSD_Version__ >= 104230000) # include extern struct callout ipf_slowtimer_ch; #endif /* END OF INCLUDES */ #if !defined(lint) static const char sccsid[] = "@(#)fil.c 1.36 6/5/96 (C) 1993-2000 Darren Reed"; -static const char rcsid[] = "@(#)$FreeBSD$"; /* static const char rcsid[] = "@(#)$Id: fil.c,v 2.243.2.125 2007/10/10 09:27:20 darrenr Exp $"; */ #endif #ifndef _KERNEL # include "ipf.h" # include "ipt.h" extern int opts; extern int blockreason; #endif /* _KERNEL */ #define FASTROUTE_RECURSION #define LBUMP(x) softc->x++ #define LBUMPD(x, y) do { softc->x.y++; DT(y); } while (0) static inline int ipf_check_ipf(fr_info_t *, frentry_t *, int); static u_32_t ipf_checkcipso(fr_info_t *, u_char *, int); static u_32_t ipf_checkripso(u_char *); static u_32_t ipf_decaps(fr_info_t *, u_32_t, int); #ifdef IPFILTER_LOG static frentry_t *ipf_dolog(fr_info_t *, u_32_t *); #endif static int ipf_flushlist(ipf_main_softc_t *, int *, frentry_t **); static int ipf_flush_groups(ipf_main_softc_t *, frgroup_t **, int); static ipfunc_t ipf_findfunc(ipfunc_t); static void *ipf_findlookup(ipf_main_softc_t *, int, frentry_t *, i6addr_t *, i6addr_t *); static frentry_t *ipf_firewall(fr_info_t *, u_32_t *); static int ipf_fr_matcharray(fr_info_t *, int *); static int ipf_frruleiter(ipf_main_softc_t *, void *, int, void *); static void ipf_funcfini(ipf_main_softc_t *, frentry_t *); static int ipf_funcinit(ipf_main_softc_t *, frentry_t *); static int ipf_geniter(ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *); static void ipf_getstat(ipf_main_softc_t *, struct friostat *, int); static int ipf_group_flush(ipf_main_softc_t *, frgroup_t *); static void ipf_group_free(frgroup_t *); static int ipf_grpmapfini(struct ipf_main_softc_s *, frentry_t *); static int ipf_grpmapinit(struct ipf_main_softc_s *, frentry_t *); static frentry_t *ipf_nextrule(ipf_main_softc_t *, int, int, frentry_t *, int); static int ipf_portcheck(frpcmp_t *, u_32_t); static inline int ipf_pr_ah(fr_info_t *); static inline void ipf_pr_esp(fr_info_t *); static inline void ipf_pr_gre(fr_info_t *); static inline void ipf_pr_udp(fr_info_t *); static inline void ipf_pr_tcp(fr_info_t *); static inline void ipf_pr_icmp(fr_info_t *); static inline void ipf_pr_ipv4hdr(fr_info_t *); static inline void ipf_pr_short(fr_info_t *, int); static inline int ipf_pr_tcpcommon(fr_info_t *); static inline int ipf_pr_udpcommon(fr_info_t *); static void ipf_rule_delete(ipf_main_softc_t *, frentry_t *f, int, int); static void ipf_rule_expire_insert(ipf_main_softc_t *, frentry_t *, int); static int ipf_synclist(ipf_main_softc_t *, frentry_t *, void *); static void ipf_token_flush(ipf_main_softc_t *); static void ipf_token_unlink(ipf_main_softc_t *, ipftoken_t *); static ipftuneable_t *ipf_tune_findbyname(ipftuneable_t *, const char *); static ipftuneable_t *ipf_tune_findbycookie(ipftuneable_t **, void *, void **); static int ipf_updateipid(fr_info_t *); static int ipf_settimeout(struct ipf_main_softc_s *, struct ipftuneable *, ipftuneval_t *); #if !defined(_KERNEL) || SOLARIS static int ppsratecheck(struct timeval *, int *, int); #endif /* * bit values for identifying presence of individual IP options * All of these tables should be ordered by increasing key value on the left * hand side to allow for binary searching of the array and include a trailer * with a 0 for the bitmask for linear searches to easily find the end with. */ static const struct optlist ipopts[] = { { IPOPT_NOP, 0x000001 }, { IPOPT_RR, 0x000002 }, { IPOPT_ZSU, 0x000004 }, { IPOPT_MTUP, 0x000008 }, { IPOPT_MTUR, 0x000010 }, { IPOPT_ENCODE, 0x000020 }, { IPOPT_TS, 0x000040 }, { IPOPT_TR, 0x000080 }, { IPOPT_SECURITY, 0x000100 }, { IPOPT_LSRR, 0x000200 }, { IPOPT_E_SEC, 0x000400 }, { IPOPT_CIPSO, 0x000800 }, { IPOPT_SATID, 0x001000 }, { IPOPT_SSRR, 0x002000 }, { IPOPT_ADDEXT, 0x004000 }, { IPOPT_VISA, 0x008000 }, { IPOPT_IMITD, 0x010000 }, { IPOPT_EIP, 0x020000 }, { IPOPT_FINN, 0x040000 }, { 0, 0x000000 } }; #ifdef USE_INET6 static const struct optlist ip6exthdr[] = { { IPPROTO_HOPOPTS, 0x000001 }, { IPPROTO_IPV6, 0x000002 }, { IPPROTO_ROUTING, 0x000004 }, { IPPROTO_FRAGMENT, 0x000008 }, { IPPROTO_ESP, 0x000010 }, { IPPROTO_AH, 0x000020 }, { IPPROTO_NONE, 0x000040 }, { IPPROTO_DSTOPTS, 0x000080 }, { IPPROTO_MOBILITY, 0x000100 }, { 0, 0 } }; #endif /* * bit values for identifying presence of individual IP security options */ static const struct optlist secopt[] = { { IPSO_CLASS_RES4, 0x01 }, { IPSO_CLASS_TOPS, 0x02 }, { IPSO_CLASS_SECR, 0x04 }, { IPSO_CLASS_RES3, 0x08 }, { IPSO_CLASS_CONF, 0x10 }, { IPSO_CLASS_UNCL, 0x20 }, { IPSO_CLASS_RES2, 0x40 }, { IPSO_CLASS_RES1, 0x80 } }; char ipfilter_version[] = IPL_VERSION; int ipf_features = 0 #ifdef IPFILTER_LKM | IPF_FEAT_LKM #endif #ifdef IPFILTER_LOG | IPF_FEAT_LOG #endif | IPF_FEAT_LOOKUP #ifdef IPFILTER_BPF | IPF_FEAT_BPF #endif #ifdef IPFILTER_COMPILED | IPF_FEAT_COMPILED #endif #ifdef IPFILTER_CKSUM | IPF_FEAT_CKSUM #endif | IPF_FEAT_SYNC #ifdef IPFILTER_SCAN | IPF_FEAT_SCAN #endif #ifdef USE_INET6 | IPF_FEAT_IPV6 #endif ; /* * Table of functions available for use with call rules. */ static ipfunc_resolve_t ipf_availfuncs[] = { { "srcgrpmap", ipf_srcgrpmap, ipf_grpmapinit, ipf_grpmapfini }, { "dstgrpmap", ipf_dstgrpmap, ipf_grpmapinit, ipf_grpmapfini }, { "", NULL, NULL, NULL } }; static ipftuneable_t ipf_main_tuneables[] = { { { (void *)offsetof(struct ipf_main_softc_s, ipf_flags) }, "ipf_flags", 0, 0xffffffff, stsizeof(ipf_main_softc_t, ipf_flags), 0, NULL, NULL }, { { (void *)offsetof(struct ipf_main_softc_s, ipf_active) }, "active", 0, 0, stsizeof(ipf_main_softc_t, ipf_active), IPFT_RDONLY, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_control_forwarding) }, "control_forwarding", 0, 1, stsizeof(ipf_main_softc_t, ipf_control_forwarding), 0, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_update_ipid) }, "update_ipid", 0, 1, stsizeof(ipf_main_softc_t, ipf_update_ipid), 0, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_chksrc) }, "chksrc", 0, 1, stsizeof(ipf_main_softc_t, ipf_chksrc), 0, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_minttl) }, "min_ttl", 0, 1, stsizeof(ipf_main_softc_t, ipf_minttl), 0, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_icmpminfragmtu) }, "icmp_minfragmtu", 0, 1, stsizeof(ipf_main_softc_t, ipf_icmpminfragmtu), 0, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_pass) }, "default_pass", 0, 0xffffffff, stsizeof(ipf_main_softc_t, ipf_pass), 0, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcpidletimeout) }, "tcp_idle_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcpidletimeout), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcpclosewait) }, "tcp_close_wait", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcpclosewait), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcplastack) }, "tcp_last_ack", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcplastack), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcptimeout) }, "tcp_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcptimeout), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcpsynsent) }, "tcp_syn_sent", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcpsynsent), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcpsynrecv) }, "tcp_syn_received", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcpsynrecv), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcpclosed) }, "tcp_closed", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcpclosed), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcphalfclosed) }, "tcp_half_closed", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcphalfclosed), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcptimewait) }, "tcp_time_wait", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcptimewait), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_udptimeout) }, "udp_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_udptimeout), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_udpacktimeout) }, "udp_ack_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_udpacktimeout), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_icmptimeout) }, "icmp_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_icmptimeout), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_icmpacktimeout) }, "icmp_ack_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_icmpacktimeout), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_iptimeout) }, "ip_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_iptimeout), 0, NULL, ipf_settimeout }, #if defined(INSTANCES) && defined(_KERNEL) { { (void *)offsetof(ipf_main_softc_t, ipf_get_loopback) }, "intercept_loopback", 0, 1, stsizeof(ipf_main_softc_t, ipf_get_loopback), 0, NULL, ipf_set_loopback }, #endif { { 0 }, NULL, 0, 0, 0, 0, NULL, NULL } }; /* * The next section of code is a collection of small routines that set * fields in the fr_info_t structure passed based on properties of the * current packet. There are different routines for the same protocol * for each of IPv4 and IPv6. Adding a new protocol, for which there * will "special" inspection for setup, is now more easily done by adding * a new routine and expanding the ipf_pr_ipinit*() function rather than by * adding more code to a growing switch statement. */ #ifdef USE_INET6 static inline int ipf_pr_ah6(fr_info_t *); static inline void ipf_pr_esp6(fr_info_t *); static inline void ipf_pr_gre6(fr_info_t *); static inline void ipf_pr_udp6(fr_info_t *); static inline void ipf_pr_tcp6(fr_info_t *); static inline void ipf_pr_icmp6(fr_info_t *); static inline void ipf_pr_ipv6hdr(fr_info_t *); static inline void ipf_pr_short6(fr_info_t *, int); static inline int ipf_pr_hopopts6(fr_info_t *); static inline int ipf_pr_mobility6(fr_info_t *); static inline int ipf_pr_routing6(fr_info_t *); static inline int ipf_pr_dstopts6(fr_info_t *); static inline int ipf_pr_fragment6(fr_info_t *); static inline struct ip6_ext *ipf_pr_ipv6exthdr(fr_info_t *, int, int); /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_short6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* xmin(I) - minimum header size */ /* */ /* IPv6 Only */ /* This is function enforces the 'is a packet too short to be legit' rule */ /* for IPv6 and marks the packet with FI_SHORT if so. See function comment */ /* for ipf_pr_short() for more details. */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_short6(fr_info_t *fin, int xmin) { if (fin->fin_dlen < xmin) fin->fin_flx |= FI_SHORT; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_ipv6hdr */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* Copy values from the IPv6 header into the fr_info_t struct and call the */ /* per-protocol analyzer if it exists. In validating the packet, a protocol*/ /* analyzer may pullup or free the packet itself so we need to be vigiliant */ /* of that possibility arising. */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_ipv6hdr(fr_info_t *fin) { ip6_t *ip6 = (ip6_t *)fin->fin_ip; int p, go = 1, i, hdrcount; fr_ip_t *fi = &fin->fin_fi; fin->fin_off = 0; fi->fi_tos = 0; fi->fi_optmsk = 0; fi->fi_secmsk = 0; fi->fi_auth = 0; p = ip6->ip6_nxt; fin->fin_crc = p; fi->fi_ttl = ip6->ip6_hlim; fi->fi_src.in6 = ip6->ip6_src; fin->fin_crc += fi->fi_src.i6[0]; fin->fin_crc += fi->fi_src.i6[1]; fin->fin_crc += fi->fi_src.i6[2]; fin->fin_crc += fi->fi_src.i6[3]; fi->fi_dst.in6 = ip6->ip6_dst; fin->fin_crc += fi->fi_dst.i6[0]; fin->fin_crc += fi->fi_dst.i6[1]; fin->fin_crc += fi->fi_dst.i6[2]; fin->fin_crc += fi->fi_dst.i6[3]; fin->fin_id = 0; if (IN6_IS_ADDR_MULTICAST(&fi->fi_dst.in6)) fin->fin_flx |= FI_MULTICAST|FI_MBCAST; hdrcount = 0; while (go && !(fin->fin_flx & FI_SHORT)) { switch (p) { case IPPROTO_UDP : ipf_pr_udp6(fin); go = 0; break; case IPPROTO_TCP : ipf_pr_tcp6(fin); go = 0; break; case IPPROTO_ICMPV6 : ipf_pr_icmp6(fin); go = 0; break; case IPPROTO_GRE : ipf_pr_gre6(fin); go = 0; break; case IPPROTO_HOPOPTS : p = ipf_pr_hopopts6(fin); break; case IPPROTO_MOBILITY : p = ipf_pr_mobility6(fin); break; case IPPROTO_DSTOPTS : p = ipf_pr_dstopts6(fin); break; case IPPROTO_ROUTING : p = ipf_pr_routing6(fin); break; case IPPROTO_AH : p = ipf_pr_ah6(fin); break; case IPPROTO_ESP : ipf_pr_esp6(fin); go = 0; break; case IPPROTO_IPV6 : for (i = 0; ip6exthdr[i].ol_bit != 0; i++) if (ip6exthdr[i].ol_val == p) { fin->fin_flx |= ip6exthdr[i].ol_bit; break; } go = 0; break; case IPPROTO_NONE : go = 0; break; case IPPROTO_FRAGMENT : p = ipf_pr_fragment6(fin); /* * Given that the only fragments we want to let through * (where fin_off != 0) are those where the non-first * fragments only have data, we can safely stop looking * at headers if this is a non-leading fragment. */ if (fin->fin_off != 0) go = 0; break; default : go = 0; break; } hdrcount++; /* * It is important to note that at this point, for the * extension headers (go != 0), the entire header may not have * been pulled up when the code gets to this point. This is * only done for "go != 0" because the other header handlers * will all pullup their complete header. The other indicator * of an incomplete packet is that this was just an extension * header. */ if ((go != 0) && (p != IPPROTO_NONE) && (ipf_pr_pullup(fin, 0) == -1)) { p = IPPROTO_NONE; break; } } /* * Some of the above functions, like ipf_pr_esp6(), can call ipf_pullup * and destroy whatever packet was here. The caller of this function * expects us to return if there is a problem with ipf_pullup. */ if (fin->fin_m == NULL) { ipf_main_softc_t *softc = fin->fin_main_soft; LBUMPD(ipf_stats[fin->fin_out], fr_v6_bad); return; } fi->fi_p = p; /* * IPv6 fragment case 1 - see comment for ipf_pr_fragment6(). * "go != 0" implies the above loop hasn't arrived at a layer 4 header. */ if ((go != 0) && (fin->fin_flx & FI_FRAG) && (fin->fin_off == 0)) { ipf_main_softc_t *softc = fin->fin_main_soft; fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_ipv6_frag_1, fr_info_t *, fin, int, go); LBUMPD(ipf_stats[fin->fin_out], fr_v6_badfrag); LBUMP(ipf_stats[fin->fin_out].fr_v6_bad); } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_ipv6exthdr */ /* Returns: struct ip6_ext * - pointer to the start of the next header */ /* or NULL if there is a prolblem. */ /* Parameters: fin(I) - pointer to packet information */ /* multiple(I) - flag indicating yes/no if multiple occurances */ /* of this extension header are allowed. */ /* proto(I) - protocol number for this extension header */ /* */ /* IPv6 Only */ /* This function embodies a number of common checks that all IPv6 extension */ /* headers must be subjected to. For example, making sure the packet is */ /* big enough for it to be in, checking if it is repeated and setting a */ /* flag to indicate its presence. */ /* ------------------------------------------------------------------------ */ static inline struct ip6_ext * ipf_pr_ipv6exthdr(fr_info_t *fin, int multiple, int proto) { ipf_main_softc_t *softc = fin->fin_main_soft; struct ip6_ext *hdr; u_short shift; int i; fin->fin_flx |= FI_V6EXTHDR; /* 8 is default length of extension hdr */ if ((fin->fin_dlen - 8) < 0) { fin->fin_flx |= FI_SHORT; LBUMPD(ipf_stats[fin->fin_out], fr_v6_ext_short); return (NULL); } if (ipf_pr_pullup(fin, 8) == -1) { LBUMPD(ipf_stats[fin->fin_out], fr_v6_ext_pullup); return (NULL); } hdr = fin->fin_dp; switch (proto) { case IPPROTO_FRAGMENT : shift = 8; break; default : shift = 8 + (hdr->ip6e_len << 3); break; } if (shift > fin->fin_dlen) { /* Nasty extension header length? */ fin->fin_flx |= FI_BAD; DT3(ipf_fi_bad_pr_ipv6exthdr_len, fr_info_t *, fin, u_short, shift, u_short, fin->fin_dlen); LBUMPD(ipf_stats[fin->fin_out], fr_v6_ext_hlen); return (NULL); } fin->fin_dp = (char *)fin->fin_dp + shift; fin->fin_dlen -= shift; /* * If we have seen a fragment header, do not set any flags to indicate * the presence of this extension header as it has no impact on the * end result until after it has been defragmented. */ if (fin->fin_flx & FI_FRAG) return (hdr); for (i = 0; ip6exthdr[i].ol_bit != 0; i++) if (ip6exthdr[i].ol_val == proto) { /* * Most IPv6 extension headers are only allowed once. */ if ((multiple == 0) && ((fin->fin_optmsk & ip6exthdr[i].ol_bit) != 0)) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_ipv6exthdr_once, fr_info_t *, fin, u_int, (fin->fin_optmsk & ip6exthdr[i].ol_bit)); } else fin->fin_optmsk |= ip6exthdr[i].ol_bit; break; } return (hdr); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_hopopts6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* This is function checks pending hop by hop options extension header */ /* ------------------------------------------------------------------------ */ static inline int ipf_pr_hopopts6(fr_info_t *fin) { struct ip6_ext *hdr; hdr = ipf_pr_ipv6exthdr(fin, 0, IPPROTO_HOPOPTS); if (hdr == NULL) return (IPPROTO_NONE); return (hdr->ip6e_nxt); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_mobility6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* This is function checks the IPv6 mobility extension header */ /* ------------------------------------------------------------------------ */ static inline int ipf_pr_mobility6(fr_info_t *fin) { struct ip6_ext *hdr; hdr = ipf_pr_ipv6exthdr(fin, 0, IPPROTO_MOBILITY); if (hdr == NULL) return (IPPROTO_NONE); return (hdr->ip6e_nxt); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_routing6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* This is function checks pending routing extension header */ /* ------------------------------------------------------------------------ */ static inline int ipf_pr_routing6(fr_info_t *fin) { struct ip6_routing *hdr; hdr = (struct ip6_routing *)ipf_pr_ipv6exthdr(fin, 0, IPPROTO_ROUTING); if (hdr == NULL) return (IPPROTO_NONE); switch (hdr->ip6r_type) { case 0 : /* * Nasty extension header length? */ if (((hdr->ip6r_len >> 1) < hdr->ip6r_segleft) || (hdr->ip6r_segleft && (hdr->ip6r_len & 1))) { ipf_main_softc_t *softc = fin->fin_main_soft; fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_routing6, fr_info_t *, fin); LBUMPD(ipf_stats[fin->fin_out], fr_v6_rh_bad); return (IPPROTO_NONE); } break; default : break; } return (hdr->ip6r_nxt); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_fragment6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* Examine the IPv6 fragment header and extract fragment offset information.*/ /* */ /* Fragments in IPv6 are extraordinarily difficult to deal with - much more */ /* so than in IPv4. There are 5 cases of fragments with IPv6 that all */ /* packets with a fragment header can fit into. They are as follows: */ /* */ /* 1. [IPv6][0-n EH][FH][0-n EH] (no L4HDR present) */ /* 2. [IPV6][0-n EH][FH][0-n EH][L4HDR part] (short) */ /* 3. [IPV6][0-n EH][FH][L4HDR part][0-n data] (short) */ /* 4. [IPV6][0-n EH][FH][0-n EH][L4HDR][0-n data] */ /* 5. [IPV6][0-n EH][FH][data] */ /* */ /* IPV6 = IPv6 header, FH = Fragment Header, */ /* 0-n EH = 0 or more extension headers, 0-n data = 0 or more bytes of data */ /* */ /* Packets that match 1, 2, 3 will be dropped as the only reasonable */ /* scenario in which they happen is in extreme circumstances that are most */ /* likely to be an indication of an attack rather than normal traffic. */ /* A type 3 packet may be sent by an attacked after a type 4 packet. There */ /* are two rules that can be used to guard against type 3 packets: L4 */ /* headers must always be in a packet that has the offset field set to 0 */ /* and no packet is allowed to overlay that where offset = 0. */ /* ------------------------------------------------------------------------ */ static inline int ipf_pr_fragment6(fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; struct ip6_frag *frag; fin->fin_flx |= FI_FRAG; frag = (struct ip6_frag *)ipf_pr_ipv6exthdr(fin, 0, IPPROTO_FRAGMENT); if (frag == NULL) { LBUMPD(ipf_stats[fin->fin_out], fr_v6_frag_bad); return (IPPROTO_NONE); } if ((frag->ip6f_offlg & IP6F_MORE_FRAG) != 0) { /* * Any fragment that isn't the last fragment must have its * length as a multiple of 8. */ if ((fin->fin_plen & 7) != 0) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_frag_not_8, fr_info_t *, fin, u_int, (fin->fin_plen & 7)); } } fin->fin_fraghdr = frag; fin->fin_id = frag->ip6f_ident; fin->fin_off = ntohs(frag->ip6f_offlg & IP6F_OFF_MASK); if (fin->fin_off != 0) fin->fin_flx |= FI_FRAGBODY; /* * Jumbograms aren't handled, so the max. length is 64k */ if ((fin->fin_off << 3) + fin->fin_dlen > 65535) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_jumbogram, fr_info_t *, fin, u_int, ((fin->fin_off << 3) + fin->fin_dlen)); } /* * We don't know where the transport layer header (or whatever is next * is), as it could be behind destination options (amongst others) so * return the fragment header as the type of packet this is. Note that * this effectively disables the fragment cache for > 1 protocol at a * time. */ return (frag->ip6f_nxt); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_dstopts6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* This is function checks pending destination options extension header */ /* ------------------------------------------------------------------------ */ static inline int ipf_pr_dstopts6(fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; struct ip6_ext *hdr; hdr = ipf_pr_ipv6exthdr(fin, 0, IPPROTO_DSTOPTS); if (hdr == NULL) { LBUMPD(ipf_stats[fin->fin_out], fr_v6_dst_bad); return (IPPROTO_NONE); } return (hdr->ip6e_nxt); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_icmp6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* This routine is mainly concerned with determining the minimum valid size */ /* for an ICMPv6 packet. */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_icmp6(fr_info_t *fin) { int minicmpsz = sizeof(struct icmp6_hdr); struct icmp6_hdr *icmp6; if (ipf_pr_pullup(fin, ICMP6ERR_MINPKTLEN - sizeof(ip6_t)) == -1) { ipf_main_softc_t *softc = fin->fin_main_soft; LBUMPD(ipf_stats[fin->fin_out], fr_v6_icmp6_pullup); return; } if (fin->fin_dlen > 1) { ip6_t *ip6; icmp6 = fin->fin_dp; fin->fin_data[0] = *(u_short *)icmp6; if ((icmp6->icmp6_type & ICMP6_INFOMSG_MASK) != 0) fin->fin_flx |= FI_ICMPQUERY; switch (icmp6->icmp6_type) { case ICMP6_ECHO_REPLY : case ICMP6_ECHO_REQUEST : if (fin->fin_dlen >= 6) fin->fin_data[1] = icmp6->icmp6_id; minicmpsz = ICMP6ERR_MINPKTLEN - sizeof(ip6_t); break; case ICMP6_DST_UNREACH : case ICMP6_PACKET_TOO_BIG : case ICMP6_TIME_EXCEEDED : case ICMP6_PARAM_PROB : fin->fin_flx |= FI_ICMPERR; minicmpsz = ICMP6ERR_IPICMPHLEN - sizeof(ip6_t); if (fin->fin_plen < ICMP6ERR_IPICMPHLEN) break; if (M_LEN(fin->fin_m) < fin->fin_plen) { if (ipf_coalesce(fin) != 1) return; } if (ipf_pr_pullup(fin, ICMP6ERR_MINPKTLEN) == -1) return; /* * If the destination of this packet doesn't match the * source of the original packet then this packet is * not correct. */ icmp6 = fin->fin_dp; ip6 = (ip6_t *)((char *)icmp6 + ICMPERR_ICMPHLEN); if (IP6_NEQ(&fin->fin_fi.fi_dst, (i6addr_t *)&ip6->ip6_src)) { fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_icmp6, fr_info_t *, fin); } break; default : break; } } ipf_pr_short6(fin, minicmpsz); if ((fin->fin_flx & (FI_SHORT|FI_BAD)) == 0) { u_char p = fin->fin_p; fin->fin_p = IPPROTO_ICMPV6; ipf_checkv6sum(fin); fin->fin_p = p; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_udp6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* Analyse the packet for IPv6/UDP properties. */ /* Is not expected to be called for fragmented packets. */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_udp6(fr_info_t *fin) { if (ipf_pr_udpcommon(fin) == 0) { u_char p = fin->fin_p; fin->fin_p = IPPROTO_UDP; ipf_checkv6sum(fin); fin->fin_p = p; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_tcp6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* Analyse the packet for IPv6/TCP properties. */ /* Is not expected to be called for fragmented packets. */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_tcp6(fr_info_t *fin) { if (ipf_pr_tcpcommon(fin) == 0) { u_char p = fin->fin_p; fin->fin_p = IPPROTO_TCP; ipf_checkv6sum(fin); fin->fin_p = p; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_esp6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* Analyse the packet for ESP properties. */ /* The minimum length is taken to be the SPI (32bits) plus a tail (32bits) */ /* even though the newer ESP packets must also have a sequence number that */ /* is 32bits as well, it is not possible(?) to determine the version from a */ /* simple packet header. */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_esp6(fr_info_t *fin) { if ((fin->fin_off == 0) && (ipf_pr_pullup(fin, 8) == -1)) { ipf_main_softc_t *softc = fin->fin_main_soft; LBUMPD(ipf_stats[fin->fin_out], fr_v6_esp_pullup); return; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_ah6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* Analyse the packet for AH properties. */ /* The minimum length is taken to be the combination of all fields in the */ /* header being present and no authentication data (null algorithm used.) */ /* ------------------------------------------------------------------------ */ static inline int ipf_pr_ah6(fr_info_t *fin) { authhdr_t *ah; fin->fin_flx |= FI_AH; ah = (authhdr_t *)ipf_pr_ipv6exthdr(fin, 0, IPPROTO_HOPOPTS); if (ah == NULL) { ipf_main_softc_t *softc = fin->fin_main_soft; LBUMPD(ipf_stats[fin->fin_out], fr_v6_ah_bad); return (IPPROTO_NONE); } ipf_pr_short6(fin, sizeof(*ah)); /* * No need for another pullup, ipf_pr_ipv6exthdr() will pullup * enough data to satisfy ah_next (the very first one.) */ return (ah->ah_next); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_gre6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Analyse the packet for GRE properties. */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_gre6(fr_info_t *fin) { grehdr_t *gre; if (ipf_pr_pullup(fin, sizeof(grehdr_t)) == -1) { ipf_main_softc_t *softc = fin->fin_main_soft; LBUMPD(ipf_stats[fin->fin_out], fr_v6_gre_pullup); return; } gre = fin->fin_dp; if (GRE_REV(gre->gr_flags) == 1) fin->fin_data[0] = gre->gr_call; } #endif /* USE_INET6 */ /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_pullup */ /* Returns: int - 0 == pullup succeeded, -1 == failure */ /* Parameters: fin(I) - pointer to packet information */ /* plen(I) - length (excluding L3 header) to pullup */ /* */ /* Short inline function to cut down on code duplication to perform a call */ /* to ipf_pullup to ensure there is the required amount of data, */ /* consecutively in the packet buffer. */ /* */ /* This function pulls up 'extra' data at the location of fin_dp. fin_dp */ /* points to the first byte after the complete layer 3 header, which will */ /* include all of the known extension headers for IPv6 or options for IPv4. */ /* */ /* Since fr_pullup() expects the total length of bytes to be pulled up, it */ /* is necessary to add those we can already assume to be pulled up (fin_dp */ /* - fin_ip) to what is passed through. */ /* ------------------------------------------------------------------------ */ int ipf_pr_pullup(fr_info_t *fin, int plen) { ipf_main_softc_t *softc = fin->fin_main_soft; if (fin->fin_m != NULL) { if (fin->fin_dp != NULL) plen += (char *)fin->fin_dp - ((char *)fin->fin_ip + fin->fin_hlen); plen += fin->fin_hlen; if (M_LEN(fin->fin_m) < plen + fin->fin_ipoff) { #if defined(_KERNEL) if (ipf_pullup(fin->fin_m, fin, plen) == NULL) { DT1(ipf_pullup_fail, fr_info_t *, fin); LBUMP(ipf_stats[fin->fin_out].fr_pull[1]); fin->fin_reason = FRB_PULLUP; fin->fin_flx |= FI_BAD; return (-1); } LBUMP(ipf_stats[fin->fin_out].fr_pull[0]); #else LBUMP(ipf_stats[fin->fin_out].fr_pull[1]); /* * Fake ipf_pullup failing */ fin->fin_reason = FRB_PULLUP; *fin->fin_mp = NULL; fin->fin_m = NULL; fin->fin_ip = NULL; fin->fin_flx |= FI_BAD; return (-1); #endif } } return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_short */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* xmin(I) - minimum header size */ /* */ /* Check if a packet is "short" as defined by xmin. The rule we are */ /* applying here is that the packet must not be fragmented within the layer */ /* 4 header. That is, it must not be a fragment that has its offset set to */ /* start within the layer 4 header (hdrmin) or if it is at offset 0, the */ /* entire layer 4 header must be present (min). */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_short(fr_info_t *fin, int xmin) { if (fin->fin_off == 0) { if (fin->fin_dlen < xmin) fin->fin_flx |= FI_SHORT; } else if (fin->fin_off < xmin) { fin->fin_flx |= FI_SHORT; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_icmp */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv4 Only */ /* Do a sanity check on the packet for ICMP (v4). In nearly all cases, */ /* except extrememly bad packets, both type and code will be present. */ /* The expected minimum size of an ICMP packet is very much dependent on */ /* the type of it. */ /* */ /* XXX - other ICMP sanity checks? */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_icmp(fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; int minicmpsz = sizeof(struct icmp); icmphdr_t *icmp; ip_t *oip; ipf_pr_short(fin, ICMPERR_ICMPHLEN); if (fin->fin_off != 0) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_icmp_frag); return; } if (ipf_pr_pullup(fin, ICMPERR_ICMPHLEN) == -1) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_icmp_pullup); return; } icmp = fin->fin_dp; fin->fin_data[0] = *(u_short *)icmp; fin->fin_data[1] = icmp->icmp_id; switch (icmp->icmp_type) { case ICMP_ECHOREPLY : case ICMP_ECHO : /* Router discovery messaes - RFC 1256 */ case ICMP_ROUTERADVERT : case ICMP_ROUTERSOLICIT : fin->fin_flx |= FI_ICMPQUERY; minicmpsz = ICMP_MINLEN; break; /* * type(1) + code(1) + cksum(2) + id(2) seq(2) + * 3 * timestamp(3 * 4) */ case ICMP_TSTAMP : case ICMP_TSTAMPREPLY : fin->fin_flx |= FI_ICMPQUERY; minicmpsz = 20; break; /* * type(1) + code(1) + cksum(2) + id(2) seq(2) + * mask(4) */ case ICMP_IREQ : case ICMP_IREQREPLY : case ICMP_MASKREQ : case ICMP_MASKREPLY : fin->fin_flx |= FI_ICMPQUERY; minicmpsz = 12; break; /* * type(1) + code(1) + cksum(2) + id(2) seq(2) + ip(20+) */ case ICMP_UNREACH : #ifdef icmp_nextmtu if (icmp->icmp_code == ICMP_UNREACH_NEEDFRAG) { if (icmp->icmp_nextmtu < softc->ipf_icmpminfragmtu) { fin->fin_flx |= FI_BAD; DT3(ipf_fi_bad_icmp_nextmtu, fr_info_t *, fin, u_int, icmp->icmp_nextmtu, u_int, softc->ipf_icmpminfragmtu); } } #endif /* FALLTHROUGH */ case ICMP_SOURCEQUENCH : case ICMP_REDIRECT : case ICMP_TIMXCEED : case ICMP_PARAMPROB : fin->fin_flx |= FI_ICMPERR; if (ipf_coalesce(fin) != 1) { LBUMPD(ipf_stats[fin->fin_out], fr_icmp_coalesce); return; } /* * ICMP error packets should not be generated for IP * packets that are a fragment that isn't the first * fragment. */ oip = (ip_t *)((char *)fin->fin_dp + ICMPERR_ICMPHLEN); if ((ntohs(oip->ip_off) & IP_OFFMASK) != 0) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_icmp_err, fr_info_t, fin, u_int, (ntohs(oip->ip_off) & IP_OFFMASK)); } /* * If the destination of this packet doesn't match the * source of the original packet then this packet is * not correct. */ if (oip->ip_src.s_addr != fin->fin_daddr) { fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_src_ne_dst, fr_info_t *, fin); } break; default : break; } ipf_pr_short(fin, minicmpsz); ipf_checkv4sum(fin); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_tcpcommon */ /* Returns: int - 0 = header ok, 1 = bad packet, -1 = buffer error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* TCP header sanity checking. Look for bad combinations of TCP flags, */ /* and make some checks with how they interact with other fields. */ /* If compiled with IPFILTER_CKSUM, check to see if the TCP checksum is */ /* valid and mark the packet as bad if not. */ /* ------------------------------------------------------------------------ */ static inline int ipf_pr_tcpcommon(fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; int flags, tlen; tcphdr_t *tcp; fin->fin_flx |= FI_TCPUDP; if (fin->fin_off != 0) { LBUMPD(ipf_stats[fin->fin_out], fr_tcp_frag); return (0); } if (ipf_pr_pullup(fin, sizeof(*tcp)) == -1) { LBUMPD(ipf_stats[fin->fin_out], fr_tcp_pullup); return (-1); } tcp = fin->fin_dp; if (fin->fin_dlen > 3) { fin->fin_sport = ntohs(tcp->th_sport); fin->fin_dport = ntohs(tcp->th_dport); } if ((fin->fin_flx & FI_SHORT) != 0) { LBUMPD(ipf_stats[fin->fin_out], fr_tcp_short); return (1); } /* * Use of the TCP data offset *must* result in a value that is at * least the same size as the TCP header. */ tlen = TCP_OFF(tcp) << 2; if (tlen < sizeof(tcphdr_t)) { LBUMPD(ipf_stats[fin->fin_out], fr_tcp_small); fin->fin_flx |= FI_BAD; DT3(ipf_fi_bad_tlen, fr_info_t, fin, u_int, tlen, u_int, sizeof(tcphdr_t)); return (1); } flags = tcp->th_flags; fin->fin_tcpf = tcp->th_flags; /* * If the urgent flag is set, then the urgent pointer must * also be set and vice versa. Good TCP packets do not have * just one of these set. */ if ((flags & TH_URG) != 0 && (tcp->th_urp == 0)) { fin->fin_flx |= FI_BAD; DT3(ipf_fi_bad_th_urg, fr_info_t*, fin, u_int, (flags & TH_URG), u_int, tcp->th_urp); #if 0 } else if ((flags & TH_URG) == 0 && (tcp->th_urp != 0)) { /* * Ignore this case (#if 0) as it shows up in "real" * traffic with bogus values in the urgent pointer field. */ fin->fin_flx |= FI_BAD; DT3(ipf_fi_bad_th_urg0, fr_info_t *, fin, u_int, (flags & TH_URG), u_int, tcp->th_urp); #endif } else if (((flags & (TH_SYN|TH_FIN)) != 0) && ((flags & (TH_RST|TH_ACK)) == TH_RST)) { /* TH_FIN|TH_RST|TH_ACK seems to appear "naturally" */ fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_th_fin_rst_ack, fr_info_t, fin); #if 1 } else if (((flags & TH_SYN) != 0) && ((flags & (TH_URG|TH_PUSH)) != 0)) { /* * SYN with URG and PUSH set is not for normal TCP but it is * possible(?) with T/TCP...but who uses T/TCP? */ fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_th_syn_urg_psh, fr_info_t *, fin); #endif } else if (!(flags & TH_ACK)) { /* * If the ack bit isn't set, then either the SYN or * RST bit must be set. If the SYN bit is set, then * we expect the ACK field to be 0. If the ACK is * not set and if URG, PSH or FIN are set, consdier * that to indicate a bad TCP packet. */ if ((flags == TH_SYN) && (tcp->th_ack != 0)) { /* * Cisco PIX sets the ACK field to a random value. * In light of this, do not set FI_BAD until a patch * is available from Cisco to ensure that * interoperability between existing systems is * achieved. */ /*fin->fin_flx |= FI_BAD*/; /*DT1(ipf_fi_bad_th_syn_ack, fr_info_t *, fin);*/ } else if (!(flags & (TH_RST|TH_SYN))) { fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_th_rst_syn, fr_info_t *, fin); } else if ((flags & (TH_URG|TH_PUSH|TH_FIN)) != 0) { fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_th_urg_push_fin, fr_info_t *, fin); } } if (fin->fin_flx & FI_BAD) { LBUMPD(ipf_stats[fin->fin_out], fr_tcp_bad_flags); return (1); } /* * At this point, it's not exactly clear what is to be gained by * marking up which TCP options are and are not present. The one we * are most interested in is the TCP window scale. This is only in * a SYN packet [RFC1323] so we don't need this here...? * Now if we were to analyse the header for passive fingerprinting, * then that might add some weight to adding this... */ if (tlen == sizeof(tcphdr_t)) { return (0); } if (ipf_pr_pullup(fin, tlen) == -1) { LBUMPD(ipf_stats[fin->fin_out], fr_tcp_pullup); return (-1); } #if 0 tcp = fin->fin_dp; ip = fin->fin_ip; s = (u_char *)(tcp + 1); off = IP_HL(ip) << 2; # ifdef _KERNEL if (fin->fin_mp != NULL) { mb_t *m = *fin->fin_mp; if (off + tlen > M_LEN(m)) return; } # endif for (tlen -= (int)sizeof(*tcp); tlen > 0; ) { opt = *s; if (opt == '\0') break; else if (opt == TCPOPT_NOP) ol = 1; else { if (tlen < 2) break; ol = (int)*(s + 1); if (ol < 2 || ol > tlen) break; } for (i = 9, mv = 4; mv >= 0; ) { op = ipopts + i; if (opt == (u_char)op->ol_val) { optmsk |= op->ol_bit; break; } } tlen -= ol; s += ol; } #endif /* 0 */ return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_udpcommon */ /* Returns: int - 0 = header ok, 1 = bad packet */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Extract the UDP source and destination ports, if present. If compiled */ /* with IPFILTER_CKSUM, check to see if the UDP checksum is valid. */ /* ------------------------------------------------------------------------ */ static inline int ipf_pr_udpcommon(fr_info_t *fin) { udphdr_t *udp; fin->fin_flx |= FI_TCPUDP; if (!fin->fin_off && (fin->fin_dlen > 3)) { if (ipf_pr_pullup(fin, sizeof(*udp)) == -1) { ipf_main_softc_t *softc = fin->fin_main_soft; fin->fin_flx |= FI_SHORT; LBUMPD(ipf_stats[fin->fin_out], fr_udp_pullup); return (1); } udp = fin->fin_dp; fin->fin_sport = ntohs(udp->uh_sport); fin->fin_dport = ntohs(udp->uh_dport); } return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_tcp */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv4 Only */ /* Analyse the packet for IPv4/TCP properties. */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_tcp(fr_info_t *fin) { ipf_pr_short(fin, sizeof(tcphdr_t)); if (ipf_pr_tcpcommon(fin) == 0) ipf_checkv4sum(fin); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_udp */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv4 Only */ /* Analyse the packet for IPv4/UDP properties. */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_udp(fr_info_t *fin) { ipf_pr_short(fin, sizeof(udphdr_t)); if (ipf_pr_udpcommon(fin) == 0) ipf_checkv4sum(fin); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_esp */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Analyse the packet for ESP properties. */ /* The minimum length is taken to be the SPI (32bits) plus a tail (32bits) */ /* even though the newer ESP packets must also have a sequence number that */ /* is 32bits as well, it is not possible(?) to determine the version from a */ /* simple packet header. */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_esp(fr_info_t *fin) { if (fin->fin_off == 0) { ipf_pr_short(fin, 8); if (ipf_pr_pullup(fin, 8) == -1) { ipf_main_softc_t *softc = fin->fin_main_soft; LBUMPD(ipf_stats[fin->fin_out], fr_v4_esp_pullup); } } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_ah */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Analyse the packet for AH properties. */ /* The minimum length is taken to be the combination of all fields in the */ /* header being present and no authentication data (null algorithm used.) */ /* ------------------------------------------------------------------------ */ static inline int ipf_pr_ah(fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; authhdr_t *ah; int len; fin->fin_flx |= FI_AH; ipf_pr_short(fin, sizeof(*ah)); if (((fin->fin_flx & FI_SHORT) != 0) || (fin->fin_off != 0)) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_ah_bad); return (IPPROTO_NONE); } if (ipf_pr_pullup(fin, sizeof(*ah)) == -1) { DT(fr_v4_ah_pullup_1); LBUMP(ipf_stats[fin->fin_out].fr_v4_ah_pullup); return (IPPROTO_NONE); } ah = (authhdr_t *)fin->fin_dp; len = (ah->ah_plen + 2) << 2; ipf_pr_short(fin, len); if (ipf_pr_pullup(fin, len) == -1) { DT(fr_v4_ah_pullup_2); LBUMP(ipf_stats[fin->fin_out].fr_v4_ah_pullup); return (IPPROTO_NONE); } /* * Adjust fin_dp and fin_dlen for skipping over the authentication * header. */ fin->fin_dp = (char *)fin->fin_dp + len; fin->fin_dlen -= len; return (ah->ah_next); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_gre */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Analyse the packet for GRE properties. */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_gre(fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; grehdr_t *gre; ipf_pr_short(fin, sizeof(grehdr_t)); if (fin->fin_off != 0) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_gre_frag); return; } if (ipf_pr_pullup(fin, sizeof(grehdr_t)) == -1) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_gre_pullup); return; } gre = fin->fin_dp; if (GRE_REV(gre->gr_flags) == 1) fin->fin_data[0] = gre->gr_call; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_ipv4hdr */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv4 Only */ /* Analyze the IPv4 header and set fields in the fr_info_t structure. */ /* Check all options present and flag their presence if any exist. */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_ipv4hdr(fr_info_t *fin) { u_short optmsk = 0, secmsk = 0, auth = 0; int hlen, ol, mv, p, i; const struct optlist *op; u_char *s, opt; u_short off; fr_ip_t *fi; ip_t *ip; fi = &fin->fin_fi; hlen = fin->fin_hlen; ip = fin->fin_ip; p = ip->ip_p; fi->fi_p = p; fin->fin_crc = p; fi->fi_tos = ip->ip_tos; fin->fin_id = ntohs(ip->ip_id); off = ntohs(ip->ip_off); /* Get both TTL and protocol */ fi->fi_p = ip->ip_p; fi->fi_ttl = ip->ip_ttl; /* Zero out bits not used in IPv6 address */ fi->fi_src.i6[1] = 0; fi->fi_src.i6[2] = 0; fi->fi_src.i6[3] = 0; fi->fi_dst.i6[1] = 0; fi->fi_dst.i6[2] = 0; fi->fi_dst.i6[3] = 0; fi->fi_saddr = ip->ip_src.s_addr; fin->fin_crc += fi->fi_saddr; fi->fi_daddr = ip->ip_dst.s_addr; fin->fin_crc += fi->fi_daddr; if (IN_MULTICAST(ntohl(fi->fi_daddr))) fin->fin_flx |= FI_MULTICAST|FI_MBCAST; /* * set packet attribute flags based on the offset and * calculate the byte offset that it represents. */ off &= IP_MF|IP_OFFMASK; if (off != 0) { int morefrag = off & IP_MF; fi->fi_flx |= FI_FRAG; off &= IP_OFFMASK; if (off == 1 && p == IPPROTO_TCP) { fin->fin_flx |= FI_SHORT; /* RFC 3128 */ DT1(ipf_fi_tcp_frag_off_1, fr_info_t *, fin); } if (off != 0) { fin->fin_flx |= FI_FRAGBODY; off <<= 3; if ((off + fin->fin_dlen > 65535) || (fin->fin_dlen == 0) || ((morefrag != 0) && ((fin->fin_dlen & 7) != 0))) { /* * The length of the packet, starting at its * offset cannot exceed 65535 (0xffff) as the * length of an IP packet is only 16 bits. * * Any fragment that isn't the last fragment * must have a length greater than 0 and it * must be an even multiple of 8. */ fi->fi_flx |= FI_BAD; DT1(ipf_fi_bad_fragbody_gt_65535, fr_info_t *, fin); } } } fin->fin_off = off; /* * Call per-protocol setup and checking */ if (p == IPPROTO_AH) { /* * Treat AH differently because we expect there to be another * layer 4 header after it. */ p = ipf_pr_ah(fin); } switch (p) { case IPPROTO_UDP : ipf_pr_udp(fin); break; case IPPROTO_TCP : ipf_pr_tcp(fin); break; case IPPROTO_ICMP : ipf_pr_icmp(fin); break; case IPPROTO_ESP : ipf_pr_esp(fin); break; case IPPROTO_GRE : ipf_pr_gre(fin); break; } ip = fin->fin_ip; if (ip == NULL) return; /* * If it is a standard IP header (no options), set the flag fields * which relate to options to 0. */ if (hlen == sizeof(*ip)) { fi->fi_optmsk = 0; fi->fi_secmsk = 0; fi->fi_auth = 0; return; } /* * So the IP header has some IP options attached. Walk the entire * list of options present with this packet and set flags to indicate * which ones are here and which ones are not. For the somewhat out * of date and obscure security classification options, set a flag to * represent which classification is present. */ fi->fi_flx |= FI_OPTIONS; for (s = (u_char *)(ip + 1), hlen -= (int)sizeof(*ip); hlen > 0; ) { opt = *s; if (opt == '\0') break; else if (opt == IPOPT_NOP) ol = 1; else { if (hlen < 2) break; ol = (int)*(s + 1); if (ol < 2 || ol > hlen) break; } for (i = 9, mv = 4; mv >= 0; ) { op = ipopts + i; if ((opt == (u_char)op->ol_val) && (ol > 4)) { u_32_t doi; switch (opt) { case IPOPT_SECURITY : if (optmsk & op->ol_bit) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_ipopt_security, fr_info_t *, fin, u_short, (optmsk & op->ol_bit)); } else { doi = ipf_checkripso(s); secmsk = doi >> 16; auth = doi & 0xffff; } break; case IPOPT_CIPSO : if (optmsk & op->ol_bit) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_ipopt_cipso, fr_info_t *, fin, u_short, (optmsk & op->ol_bit)); } else { doi = ipf_checkcipso(fin, s, ol); secmsk = doi >> 16; auth = doi & 0xffff; } break; } optmsk |= op->ol_bit; } if (opt < op->ol_val) i -= mv; else i += mv; mv--; } hlen -= ol; s += ol; } /* * */ if (auth && !(auth & 0x0100)) auth &= 0xff00; fi->fi_optmsk = optmsk; fi->fi_secmsk = secmsk; fi->fi_auth = auth; } /* ------------------------------------------------------------------------ */ /* Function: ipf_checkripso */ /* Returns: void */ /* Parameters: s(I) - pointer to start of RIPSO option */ /* */ /* ------------------------------------------------------------------------ */ static u_32_t ipf_checkripso(u_char *s) { const struct optlist *sp; u_short secmsk = 0, auth = 0; u_char sec; int j, m; sec = *(s + 2); /* classification */ for (j = 3, m = 2; m >= 0; ) { sp = secopt + j; if (sec == sp->ol_val) { secmsk |= sp->ol_bit; auth = *(s + 3); auth *= 256; auth += *(s + 4); break; } if (sec < sp->ol_val) j -= m; else j += m; m--; } return (secmsk << 16) | auth; } /* ------------------------------------------------------------------------ */ /* Function: ipf_checkcipso */ /* Returns: u_32_t - 0 = failure, else the doi from the header */ /* Parameters: fin(IO) - pointer to packet information */ /* s(I) - pointer to start of CIPSO option */ /* ol(I) - length of CIPSO option field */ /* */ /* This function returns the domain of integrity (DOI) field from the CIPSO */ /* header and returns that whilst also storing the highest sensitivity */ /* value found in the fr_info_t structure. */ /* */ /* No attempt is made to extract the category bitmaps as these are defined */ /* by the user (rather than the protocol) and can be rather numerous on the */ /* end nodes. */ /* ------------------------------------------------------------------------ */ static u_32_t ipf_checkcipso(fr_info_t *fin, u_char *s, int ol) { ipf_main_softc_t *softc = fin->fin_main_soft; fr_ip_t *fi; u_32_t doi; u_char *t, tag, tlen, sensitivity; int len; if (ol < 6 || ol > 40) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_cipso_bad); fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_ol, fr_info_t *, fin, u_int, ol); return (0); } fi = &fin->fin_fi; fi->fi_sensitivity = 0; /* * The DOI field MUST be there. */ bcopy(s + 2, &doi, sizeof(doi)); t = (u_char *)s + 6; for (len = ol - 6; len >= 2; len -= tlen, t+= tlen) { tag = *t; tlen = *(t + 1); if (tlen > len || tlen < 4 || tlen > 34) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_cipso_tlen); fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_tlen, fr_info_t *, fin, u_int, tlen); return (0); } sensitivity = 0; /* * Tag numbers 0, 1, 2, 5 are laid out in the CIPSO Internet * draft (16 July 1992) that has expired. */ if (tag == 0) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_tag, fr_info_t *, fin, u_int, tag); continue; } else if (tag == 1) { if (*(t + 2) != 0) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_tag1_t2, fr_info_t *, fin, u_int, (*t + 2)); continue; } sensitivity = *(t + 3); /* Category bitmap for categories 0-239 */ } else if (tag == 4) { if (*(t + 2) != 0) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_tag4_t2, fr_info_t *, fin, u_int, (*t + 2)); continue; } sensitivity = *(t + 3); /* Enumerated categories, 16bits each, upto 15 */ } else if (tag == 5) { if (*(t + 2) != 0) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_tag5_t2, fr_info_t *, fin, u_int, (*t + 2)); continue; } sensitivity = *(t + 3); /* Range of categories (2*16bits), up to 7 pairs */ } else if (tag > 127) { /* Custom defined DOI */ ; } else { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_tag127, fr_info_t *, fin, u_int, tag); continue; } if (sensitivity > fi->fi_sensitivity) fi->fi_sensitivity = sensitivity; } return (doi); } /* ------------------------------------------------------------------------ */ /* Function: ipf_makefrip */ /* Returns: int - 0 == packet ok, -1 == packet freed */ /* Parameters: hlen(I) - length of IP packet header */ /* ip(I) - pointer to the IP header */ /* fin(IO) - pointer to packet information */ /* */ /* Compact the IP header into a structure which contains just the info. */ /* which is useful for comparing IP headers with and store this information */ /* in the fr_info_t structure pointer to by fin. At present, it is assumed */ /* this function will be called with either an IPv4 or IPv6 packet. */ /* ------------------------------------------------------------------------ */ int ipf_makefrip(int hlen, ip_t *ip, fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; int v; fin->fin_depth = 0; fin->fin_hlen = (u_short)hlen; fin->fin_ip = ip; fin->fin_rule = 0xffffffff; fin->fin_group[0] = -1; fin->fin_group[1] = '\0'; fin->fin_dp = (char *)ip + hlen; v = fin->fin_v; if (v == 4) { fin->fin_plen = ntohs(ip->ip_len); fin->fin_dlen = fin->fin_plen - hlen; ipf_pr_ipv4hdr(fin); #ifdef USE_INET6 } else if (v == 6) { fin->fin_plen = ntohs(((ip6_t *)ip)->ip6_plen); fin->fin_dlen = fin->fin_plen; fin->fin_plen += hlen; ipf_pr_ipv6hdr(fin); #endif } if (fin->fin_ip == NULL) { LBUMP(ipf_stats[fin->fin_out].fr_ip_freed); return (-1); } return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_portcheck */ /* Returns: int - 1 == port matched, 0 == port match failed */ /* Parameters: frp(I) - pointer to port check `expression' */ /* pop(I) - port number to evaluate */ /* */ /* Perform a comparison of a port number against some other(s), using a */ /* structure with compare information stored in it. */ /* ------------------------------------------------------------------------ */ static inline int ipf_portcheck(frpcmp_t *frp, u_32_t pop) { int err = 1; u_32_t po; po = frp->frp_port; /* * Do opposite test to that required and continue if that succeeds. */ switch (frp->frp_cmp) { case FR_EQUAL : if (pop != po) /* EQUAL */ err = 0; break; case FR_NEQUAL : if (pop == po) /* NOTEQUAL */ err = 0; break; case FR_LESST : if (pop >= po) /* LESSTHAN */ err = 0; break; case FR_GREATERT : if (pop <= po) /* GREATERTHAN */ err = 0; break; case FR_LESSTE : if (pop > po) /* LT or EQ */ err = 0; break; case FR_GREATERTE : if (pop < po) /* GT or EQ */ err = 0; break; case FR_OUTRANGE : if (pop >= po && pop <= frp->frp_top) /* Out of range */ err = 0; break; case FR_INRANGE : if (pop <= po || pop >= frp->frp_top) /* In range */ err = 0; break; case FR_INCRANGE : if (pop < po || pop > frp->frp_top) /* Inclusive range */ err = 0; break; default : break; } return (err); } /* ------------------------------------------------------------------------ */ /* Function: ipf_tcpudpchk */ /* Returns: int - 1 == protocol matched, 0 == check failed */ /* Parameters: fda(I) - pointer to packet information */ /* ft(I) - pointer to structure with comparison data */ /* */ /* Compares the current pcket (assuming it is TCP/UDP) information with a */ /* structure containing information that we want to match against. */ /* ------------------------------------------------------------------------ */ int ipf_tcpudpchk(fr_ip_t *fi, frtuc_t *ft) { int err = 1; /* * Both ports should *always* be in the first fragment. * So far, I cannot find any cases where they can not be. * * compare destination ports */ if (ft->ftu_dcmp) err = ipf_portcheck(&ft->ftu_dst, fi->fi_ports[1]); /* * compare source ports */ if (err && ft->ftu_scmp) err = ipf_portcheck(&ft->ftu_src, fi->fi_ports[0]); /* * If we don't have all the TCP/UDP header, then how can we * expect to do any sort of match on it ? If we were looking for * TCP flags, then NO match. If not, then match (which should * satisfy the "short" class too). */ if (err && (fi->fi_p == IPPROTO_TCP)) { if (fi->fi_flx & FI_SHORT) return (!(ft->ftu_tcpf | ft->ftu_tcpfm)); /* * Match the flags ? If not, abort this match. */ if (ft->ftu_tcpfm && ft->ftu_tcpf != (fi->fi_tcpf & ft->ftu_tcpfm)) { FR_DEBUG(("f. %#x & %#x != %#x\n", fi->fi_tcpf, ft->ftu_tcpfm, ft->ftu_tcpf)); err = 0; } } return (err); } /* ------------------------------------------------------------------------ */ /* Function: ipf_check_ipf */ /* Returns: int - 0 == match, else no match */ /* Parameters: fin(I) - pointer to packet information */ /* fr(I) - pointer to filter rule */ /* portcmp(I) - flag indicating whether to attempt matching on */ /* TCP/UDP port data. */ /* */ /* Check to see if a packet matches an IPFilter rule. Checks of addresses, */ /* port numbers, etc, for "standard" IPFilter rules are all orchestrated in */ /* this function. */ /* ------------------------------------------------------------------------ */ static inline int ipf_check_ipf(fr_info_t *fin, frentry_t *fr, int portcmp) { u_32_t *ld, *lm, *lip; fripf_t *fri; fr_ip_t *fi; int i; fi = &fin->fin_fi; fri = fr->fr_ipf; lip = (u_32_t *)fi; lm = (u_32_t *)&fri->fri_mip; ld = (u_32_t *)&fri->fri_ip; /* * first 32 bits to check coversion: * IP version, TOS, TTL, protocol */ i = ((*lip & *lm) != *ld); FR_DEBUG(("0. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); if (i) return (1); /* * Next 32 bits is a constructed bitmask indicating which IP options * are present (if any) in this packet. */ lip++, lm++, ld++; i = ((*lip & *lm) != *ld); FR_DEBUG(("1. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); if (i != 0) return (1); lip++, lm++, ld++; /* * Unrolled loops (4 each, for 32 bits) for address checks. */ /* * Check the source address. */ if (fr->fr_satype == FRI_LOOKUP) { i = (*fr->fr_srcfunc)(fin->fin_main_soft, fr->fr_srcptr, fi->fi_v, lip, fin->fin_plen); if (i == -1) return (1); lip += 3; lm += 3; ld += 3; } else { i = ((*lip & *lm) != *ld); FR_DEBUG(("2a. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); if (fi->fi_v == 6) { lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("2b. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("2c. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("2d. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); } else { lip += 3; lm += 3; ld += 3; } } i ^= (fr->fr_flags & FR_NOTSRCIP) >> 6; if (i != 0) return (1); /* * Check the destination address. */ lip++, lm++, ld++; if (fr->fr_datype == FRI_LOOKUP) { i = (*fr->fr_dstfunc)(fin->fin_main_soft, fr->fr_dstptr, fi->fi_v, lip, fin->fin_plen); if (i == -1) return (1); lip += 3; lm += 3; ld += 3; } else { i = ((*lip & *lm) != *ld); FR_DEBUG(("3a. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); if (fi->fi_v == 6) { lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("3b. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("3c. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("3d. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); } else { lip += 3; lm += 3; ld += 3; } } i ^= (fr->fr_flags & FR_NOTDSTIP) >> 7; if (i != 0) return (1); /* * IP addresses matched. The next 32bits contains: * mast of old IP header security & authentication bits. */ lip++, lm++, ld++; i = (*ld - (*lip & *lm)); FR_DEBUG(("4. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); /* * Next we have 32 bits of packet flags. */ lip++, lm++, ld++; i |= (*ld - (*lip & *lm)); FR_DEBUG(("5. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); if (i == 0) { /* * If a fragment, then only the first has what we're * looking for here... */ if (portcmp) { if (!ipf_tcpudpchk(&fin->fin_fi, &fr->fr_tuc)) i = 1; } else { if (fr->fr_dcmp || fr->fr_scmp || fr->fr_tcpf || fr->fr_tcpfm) i = 1; if (fr->fr_icmpm || fr->fr_icmp) { if (((fi->fi_p != IPPROTO_ICMP) && (fi->fi_p != IPPROTO_ICMPV6)) || fin->fin_off || (fin->fin_dlen < 2)) i = 1; else if ((fin->fin_data[0] & fr->fr_icmpm) != fr->fr_icmp) { FR_DEBUG(("i. %#x & %#x != %#x\n", fin->fin_data[0], fr->fr_icmpm, fr->fr_icmp)); i = 1; } } } } return (i); } /* ------------------------------------------------------------------------ */ /* Function: ipf_scanlist */ /* Returns: int - result flags of scanning filter list */ /* Parameters: fin(I) - pointer to packet information */ /* pass(I) - default result to return for filtering */ /* */ /* Check the input/output list of rules for a match to the current packet. */ /* If a match is found, the value of fr_flags from the rule becomes the */ /* return value and fin->fin_fr points to the matched rule. */ /* */ /* This function may be called recursively upto 16 times (limit inbuilt.) */ /* When unwinding, it should finish up with fin_depth as 0. */ /* */ /* Could be per interface, but this gets real nasty when you don't have, */ /* or can't easily change, the kernel source code to . */ /* ------------------------------------------------------------------------ */ int ipf_scanlist(fr_info_t *fin, u_32_t pass) { ipf_main_softc_t *softc = fin->fin_main_soft; int rulen, portcmp, off, skip; struct frentry *fr, *fnext; u_32_t passt, passo; /* * Do not allow nesting deeper than 16 levels. */ if (fin->fin_depth >= 16) return (pass); fr = fin->fin_fr; /* * If there are no rules in this list, return now. */ if (fr == NULL) return (pass); skip = 0; portcmp = 0; fin->fin_depth++; fin->fin_fr = NULL; off = fin->fin_off; if ((fin->fin_flx & FI_TCPUDP) && (fin->fin_dlen > 3) && !off) portcmp = 1; for (rulen = 0; fr; fr = fnext, rulen++) { fnext = fr->fr_next; if (skip != 0) { FR_VERBOSE(("SKIP %d (%#x)\n", skip, fr->fr_flags)); skip--; continue; } /* * In all checks below, a null (zero) value in the * filter struture is taken to mean a wildcard. * * check that we are working for the right interface */ #ifdef _KERNEL if (fr->fr_ifa && fr->fr_ifa != fin->fin_ifp) continue; #else if (opts & (OPT_VERBOSE|OPT_DEBUG)) printf("\n"); FR_VERBOSE(("%c", FR_ISSKIP(pass) ? 's' : FR_ISPASS(pass) ? 'p' : FR_ISACCOUNT(pass) ? 'A' : FR_ISAUTH(pass) ? 'a' : (pass & FR_NOMATCH) ? 'n' :'b')); if (fr->fr_ifa && fr->fr_ifa != fin->fin_ifp) continue; FR_VERBOSE((":i")); #endif switch (fr->fr_type) { case FR_T_IPF : case FR_T_IPF_BUILTIN : if (ipf_check_ipf(fin, fr, portcmp)) continue; break; #if defined(IPFILTER_BPF) case FR_T_BPFOPC : case FR_T_BPFOPC_BUILTIN : { u_char *mc; int wlen; if (*fin->fin_mp == NULL) continue; if (fin->fin_family != fr->fr_family) continue; mc = (u_char *)fin->fin_m; wlen = fin->fin_dlen + fin->fin_hlen; if (!bpf_filter(fr->fr_data, mc, wlen, 0)) continue; break; } #endif case FR_T_CALLFUNC_BUILTIN : { frentry_t *f; f = (*fr->fr_func)(fin, &pass); if (f != NULL) fr = f; else continue; break; } case FR_T_IPFEXPR : case FR_T_IPFEXPR_BUILTIN : if (fin->fin_family != fr->fr_family) continue; if (ipf_fr_matcharray(fin, fr->fr_data) == 0) continue; break; default : break; } if ((fin->fin_out == 0) && (fr->fr_nattag.ipt_num[0] != 0)) { if (fin->fin_nattag == NULL) continue; if (ipf_matchtag(&fr->fr_nattag, fin->fin_nattag) == 0) continue; } FR_VERBOSE(("=%d/%d.%d *", fr->fr_grhead, fr->fr_group, rulen)); passt = fr->fr_flags; /* * If the rule is a "call now" rule, then call the function * in the rule, if it exists and use the results from that. * If the function pointer is bad, just make like we ignore * it, except for increasing the hit counter. */ if ((passt & FR_CALLNOW) != 0) { frentry_t *frs; ATOMIC_INC64(fr->fr_hits); if ((fr->fr_func == NULL) || (fr->fr_func == (ipfunc_t)-1)) continue; frs = fin->fin_fr; fin->fin_fr = fr; fr = (*fr->fr_func)(fin, &passt); if (fr == NULL) { fin->fin_fr = frs; continue; } passt = fr->fr_flags; } fin->fin_fr = fr; #ifdef IPFILTER_LOG /* * Just log this packet... */ if ((passt & FR_LOGMASK) == FR_LOG) { if (ipf_log_pkt(fin, passt) == -1) { if (passt & FR_LOGORBLOCK) { DT(frb_logfail); passt &= ~FR_CMDMASK; passt |= FR_BLOCK|FR_QUICK; fin->fin_reason = FRB_LOGFAIL; } } } #endif /* IPFILTER_LOG */ MUTEX_ENTER(&fr->fr_lock); fr->fr_bytes += (U_QUAD_T)fin->fin_plen; fr->fr_hits++; MUTEX_EXIT(&fr->fr_lock); fin->fin_rule = rulen; passo = pass; if (FR_ISSKIP(passt)) { skip = fr->fr_arg; continue; } else if (((passt & FR_LOGMASK) != FR_LOG) && ((passt & FR_LOGMASK) != FR_DECAPSULATE)) { pass = passt; } if (passt & (FR_RETICMP|FR_FAKEICMP)) fin->fin_icode = fr->fr_icode; if (fr->fr_group != -1) { (void) strncpy(fin->fin_group, FR_NAME(fr, fr_group), strlen(FR_NAME(fr, fr_group))); } else { fin->fin_group[0] = '\0'; } FR_DEBUG(("pass %#x/%#x/%x\n", passo, pass, passt)); if (fr->fr_grphead != NULL) { fin->fin_fr = fr->fr_grphead->fg_start; FR_VERBOSE(("group %s\n", FR_NAME(fr, fr_grhead))); if (FR_ISDECAPS(passt)) passt = ipf_decaps(fin, pass, fr->fr_icode); else passt = ipf_scanlist(fin, pass); if (fin->fin_fr == NULL) { fin->fin_rule = rulen; if (fr->fr_group != -1) (void) strncpy(fin->fin_group, fr->fr_names + fr->fr_group, strlen(fr->fr_names + fr->fr_group)); fin->fin_fr = fr; passt = pass; } pass = passt; } if (pass & FR_QUICK) { /* * Finally, if we've asked to track state for this * packet, set it up. Add state for "quick" rules * here so that if the action fails we can consider * the rule to "not match" and keep on processing * filter rules. */ if ((pass & FR_KEEPSTATE) && !FR_ISAUTH(pass) && !(fin->fin_flx & FI_STATE)) { int out = fin->fin_out; fin->fin_fr = fr; if (ipf_state_add(softc, fin, NULL, 0) == 0) { LBUMPD(ipf_stats[out], fr_ads); } else { LBUMPD(ipf_stats[out], fr_bads); pass = passo; continue; } } break; } } fin->fin_depth--; return (pass); } /* ------------------------------------------------------------------------ */ /* Function: ipf_acctpkt */ /* Returns: frentry_t* - always returns NULL */ /* Parameters: fin(I) - pointer to packet information */ /* passp(IO) - pointer to current/new filter decision (unused) */ /* */ /* Checks a packet against accounting rules, if there are any for the given */ /* IP protocol version. */ /* */ /* N.B.: this function returns NULL to match the prototype used by other */ /* functions called from the IPFilter "mainline" in ipf_check(). */ /* ------------------------------------------------------------------------ */ frentry_t * ipf_acctpkt(fr_info_t *fin, u_32_t *passp) { ipf_main_softc_t *softc = fin->fin_main_soft; char group[FR_GROUPLEN]; frentry_t *fr, *frsave; u_32_t pass, rulen; passp = passp; fr = softc->ipf_acct[fin->fin_out][softc->ipf_active]; if (fr != NULL) { frsave = fin->fin_fr; bcopy(fin->fin_group, group, FR_GROUPLEN); rulen = fin->fin_rule; fin->fin_fr = fr; pass = ipf_scanlist(fin, FR_NOMATCH); if (FR_ISACCOUNT(pass)) { LBUMPD(ipf_stats[0], fr_acct); } fin->fin_fr = frsave; bcopy(group, fin->fin_group, FR_GROUPLEN); fin->fin_rule = rulen; } return (NULL); } /* ------------------------------------------------------------------------ */ /* Function: ipf_firewall */ /* Returns: frentry_t* - returns pointer to matched rule, if no matches */ /* were found, returns NULL. */ /* Parameters: fin(I) - pointer to packet information */ /* passp(IO) - pointer to current/new filter decision (unused) */ /* */ /* Applies an appropriate set of firewall rules to the packet, to see if */ /* there are any matches. The first check is to see if a match can be seen */ /* in the cache. If not, then search an appropriate list of rules. Once a */ /* matching rule is found, take any appropriate actions as defined by the */ /* rule - except logging. */ /* ------------------------------------------------------------------------ */ static frentry_t * ipf_firewall(fr_info_t *fin, u_32_t *passp) { ipf_main_softc_t *softc = fin->fin_main_soft; frentry_t *fr; u_32_t pass; int out; out = fin->fin_out; pass = *passp; /* * This rule cache will only affect packets that are not being * statefully filtered. */ fin->fin_fr = softc->ipf_rules[out][softc->ipf_active]; if (fin->fin_fr != NULL) pass = ipf_scanlist(fin, softc->ipf_pass); if ((pass & FR_NOMATCH)) { LBUMPD(ipf_stats[out], fr_nom); } fr = fin->fin_fr; /* * Apply packets per second rate-limiting to a rule as required. */ if ((fr != NULL) && (fr->fr_pps != 0) && !ppsratecheck(&fr->fr_lastpkt, &fr->fr_curpps, fr->fr_pps)) { DT2(frb_ppsrate, fr_info_t *, fin, frentry_t *, fr); pass &= ~(FR_CMDMASK|FR_RETICMP|FR_RETRST); pass |= FR_BLOCK; LBUMPD(ipf_stats[out], fr_ppshit); fin->fin_reason = FRB_PPSRATE; } /* * If we fail to add a packet to the authorization queue, then we * drop the packet later. However, if it was added then pretend * we've dropped it already. */ if (FR_ISAUTH(pass)) { if (ipf_auth_new(fin->fin_m, fin) != 0) { DT1(frb_authnew, fr_info_t *, fin); fin->fin_m = *fin->fin_mp = NULL; fin->fin_reason = FRB_AUTHNEW; fin->fin_error = 0; } else { IPFERROR(1); fin->fin_error = ENOSPC; } } if ((fr != NULL) && (fr->fr_func != NULL) && (fr->fr_func != (ipfunc_t)-1) && !(pass & FR_CALLNOW)) (void) (*fr->fr_func)(fin, &pass); /* * If a rule is a pre-auth rule, check again in the list of rules * loaded for authenticated use. It does not particulary matter * if this search fails because a "preauth" result, from a rule, * is treated as "not a pass", hence the packet is blocked. */ if (FR_ISPREAUTH(pass)) { pass = ipf_auth_pre_scanlist(softc, fin, pass); } /* * If the rule has "keep frag" and the packet is actually a fragment, * then create a fragment state entry. */ if (pass & FR_KEEPFRAG) { if (fin->fin_flx & FI_FRAG) { if (ipf_frag_new(softc, fin, pass) == -1) { LBUMP(ipf_stats[out].fr_bnfr); } else { LBUMP(ipf_stats[out].fr_nfr); } } else { LBUMP(ipf_stats[out].fr_cfr); } } fr = fin->fin_fr; *passp = pass; return (fr); } /* ------------------------------------------------------------------------ */ /* Function: ipf_check */ /* Returns: int - 0 == packet allowed through, */ /* User space: */ /* -1 == packet blocked */ /* 1 == packet not matched */ /* -2 == requires authentication */ /* Kernel: */ /* > 0 == filter error # for packet */ /* Parameters: ctx(I) - pointer to the instance context */ /* ip(I) - pointer to start of IPv4/6 packet */ /* hlen(I) - length of header */ /* ifp(I) - pointer to interface this packet is on */ /* out(I) - 0 == packet going in, 1 == packet going out */ /* mp(IO) - pointer to caller's buffer pointer that holds this */ /* IP packet. */ /* Solaris: */ /* qpi(I) - pointer to STREAMS queue information for this */ /* interface & direction. */ /* */ /* ipf_check() is the master function for all IPFilter packet processing. */ /* It orchestrates: Network Address Translation (NAT), checking for packet */ /* authorisation (or pre-authorisation), presence of related state info., */ /* generating log entries, IP packet accounting, routing of packets as */ /* directed by firewall rules and of course whether or not to allow the */ /* packet to be further processed by the kernel. */ /* */ /* For packets blocked, the contents of "mp" will be NULL'd and the buffer */ /* freed. Packets passed may be returned with the pointer pointed to by */ /* by "mp" changed to a new buffer. */ /* ------------------------------------------------------------------------ */ int ipf_check(void *ctx, ip_t *ip, int hlen, struct ifnet *ifp, int out #if defined(_KERNEL) && SOLARIS , void* qif, mb_t **mp) #else , mb_t **mp) #endif { /* * The above really sucks, but short of writing a diff */ ipf_main_softc_t *softc = ctx; fr_info_t frinfo; fr_info_t *fin = &frinfo; u_32_t pass = softc->ipf_pass; frentry_t *fr = NULL; int v = IP_V(ip); mb_t *mc = NULL; mb_t *m; /* * The first part of ipf_check() deals with making sure that what goes * into the filtering engine makes some sense. Information about the * the packet is distilled, collected into a fr_info_t structure and * the an attempt to ensure the buffer the packet is in is big enough * to hold all the required packet headers. */ #ifdef _KERNEL # if SOLARIS qpktinfo_t *qpi = qif; # ifdef __sparc if ((u_int)ip & 0x3) return (2); # endif # else SPL_INT(s); # endif if (softc->ipf_running <= 0) { return (0); } bzero((char *)fin, sizeof(*fin)); # if SOLARIS if (qpi->qpi_flags & QF_BROADCAST) fin->fin_flx |= FI_MBCAST|FI_BROADCAST; if (qpi->qpi_flags & QF_MULTICAST) fin->fin_flx |= FI_MBCAST|FI_MULTICAST; m = qpi->qpi_m; fin->fin_qfm = m; fin->fin_qpi = qpi; # else /* SOLARIS */ m = *mp; # if defined(M_MCAST) if ((m->m_flags & M_MCAST) != 0) fin->fin_flx |= FI_MBCAST|FI_MULTICAST; # endif # if defined(M_MLOOP) if ((m->m_flags & M_MLOOP) != 0) fin->fin_flx |= FI_MBCAST|FI_MULTICAST; # endif # if defined(M_BCAST) if ((m->m_flags & M_BCAST) != 0) fin->fin_flx |= FI_MBCAST|FI_BROADCAST; # endif # ifdef M_CANFASTFWD /* * XXX For now, IP Filter and fast-forwarding of cached flows * XXX are mutually exclusive. Eventually, IP Filter should * XXX get a "can-fast-forward" filter rule. */ m->m_flags &= ~M_CANFASTFWD; # endif /* M_CANFASTFWD */ # if defined(CSUM_DELAY_DATA) && !defined(__FreeBSD__) /* * disable delayed checksums. */ if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { in_delayed_cksum(m); m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; } # endif /* CSUM_DELAY_DATA */ # endif /* SOLARIS */ #else bzero((char *)fin, sizeof(*fin)); m = *mp; # if defined(M_MCAST) if ((m->m_flags & M_MCAST) != 0) fin->fin_flx |= FI_MBCAST|FI_MULTICAST; # endif # if defined(M_MLOOP) if ((m->m_flags & M_MLOOP) != 0) fin->fin_flx |= FI_MBCAST|FI_MULTICAST; # endif # if defined(M_BCAST) if ((m->m_flags & M_BCAST) != 0) fin->fin_flx |= FI_MBCAST|FI_BROADCAST; # endif #endif /* _KERNEL */ fin->fin_v = v; fin->fin_m = m; fin->fin_ip = ip; fin->fin_mp = mp; fin->fin_out = out; fin->fin_ifp = ifp; fin->fin_error = ENETUNREACH; fin->fin_hlen = (u_short)hlen; fin->fin_dp = (char *)ip + hlen; fin->fin_main_soft = softc; fin->fin_ipoff = (char *)ip - MTOD(m, char *); SPL_NET(s); #ifdef USE_INET6 if (v == 6) { LBUMP(ipf_stats[out].fr_ipv6); /* * Jumbo grams are quite likely too big for internal buffer * structures to handle comfortably, for now, so just drop * them. */ if (((ip6_t *)ip)->ip6_plen == 0) { DT1(frb_jumbo, ip6_t *, (ip6_t *)ip); pass = FR_BLOCK|FR_NOMATCH; fin->fin_reason = FRB_JUMBO; goto finished; } fin->fin_family = AF_INET6; } else #endif { fin->fin_family = AF_INET; } if (ipf_makefrip(hlen, ip, fin) == -1) { DT1(frb_makefrip, fr_info_t *, fin); pass = FR_BLOCK|FR_NOMATCH; fin->fin_reason = FRB_MAKEFRIP; goto finished; } /* * For at least IPv6 packets, if a m_pullup() fails then this pointer * becomes NULL and so we have no packet to free. */ if (*fin->fin_mp == NULL) goto finished; if (!out) { if (v == 4) { if (softc->ipf_chksrc && !ipf_verifysrc(fin)) { LBUMPD(ipf_stats[0], fr_v4_badsrc); fin->fin_flx |= FI_BADSRC; } if (fin->fin_ip->ip_ttl < softc->ipf_minttl) { LBUMPD(ipf_stats[0], fr_v4_badttl); fin->fin_flx |= FI_LOWTTL; } } #ifdef USE_INET6 else if (v == 6) { if (((ip6_t *)ip)->ip6_hlim < softc->ipf_minttl) { LBUMPD(ipf_stats[0], fr_v6_badttl); fin->fin_flx |= FI_LOWTTL; } } #endif } if (fin->fin_flx & FI_SHORT) { LBUMPD(ipf_stats[out], fr_short); } READ_ENTER(&softc->ipf_mutex); if (!out) { switch (fin->fin_v) { case 4 : if (ipf_nat_checkin(fin, &pass) == -1) { goto filterdone; } break; #ifdef USE_INET6 case 6 : if (ipf_nat6_checkin(fin, &pass) == -1) { goto filterdone; } break; #endif default : break; } } /* * Check auth now. * If a packet is found in the auth table, then skip checking * the access lists for permission but we do need to consider * the result as if it were from the ACL's. In addition, being * found in the auth table means it has been seen before, so do * not pass it through accounting (again), lest it be counted twice. */ fr = ipf_auth_check(fin, &pass); if (!out && (fr == NULL)) (void) ipf_acctpkt(fin, NULL); if (fr == NULL) { if ((fin->fin_flx & FI_FRAG) != 0) fr = ipf_frag_known(fin, &pass); if (fr == NULL) fr = ipf_state_check(fin, &pass); } if ((pass & FR_NOMATCH) || (fr == NULL)) fr = ipf_firewall(fin, &pass); /* * If we've asked to track state for this packet, set it up. * Here rather than ipf_firewall because ipf_checkauth may decide * to return a packet for "keep state" */ if ((pass & FR_KEEPSTATE) && (fin->fin_m != NULL) && !(fin->fin_flx & FI_STATE)) { if (ipf_state_add(softc, fin, NULL, 0) == 0) { LBUMP(ipf_stats[out].fr_ads); } else { LBUMP(ipf_stats[out].fr_bads); if (FR_ISPASS(pass)) { DT(frb_stateadd); pass &= ~FR_CMDMASK; pass |= FR_BLOCK; fin->fin_reason = FRB_STATEADD; } } } fin->fin_fr = fr; if ((fr != NULL) && !(fin->fin_flx & FI_STATE)) { fin->fin_dif = &fr->fr_dif; fin->fin_tif = &fr->fr_tifs[fin->fin_rev]; } /* * Only count/translate packets which will be passed on, out the * interface. */ if (out && FR_ISPASS(pass)) { (void) ipf_acctpkt(fin, NULL); switch (fin->fin_v) { case 4 : if (ipf_nat_checkout(fin, &pass) == -1) { ; } else if ((softc->ipf_update_ipid != 0) && (v == 4)) { if (ipf_updateipid(fin) == -1) { DT(frb_updateipid); LBUMP(ipf_stats[1].fr_ipud); pass &= ~FR_CMDMASK; pass |= FR_BLOCK; fin->fin_reason = FRB_UPDATEIPID; } else { LBUMP(ipf_stats[0].fr_ipud); } } break; #ifdef USE_INET6 case 6 : (void) ipf_nat6_checkout(fin, &pass); break; #endif default : break; } } filterdone: #ifdef IPFILTER_LOG if ((softc->ipf_flags & FF_LOGGING) || (pass & FR_LOGMASK)) { (void) ipf_dolog(fin, &pass); } #endif /* * The FI_STATE flag is cleared here so that calling ipf_state_check * will work when called from inside of fr_fastroute. Although * there is a similar flag, FI_NATED, for NAT, it does have the same * impact on code execution. */ fin->fin_flx &= ~FI_STATE; #if defined(FASTROUTE_RECURSION) /* * Up the reference on fr_lock and exit ipf_mutex. The generation of * a packet below can sometimes cause a recursive call into IPFilter. * On those platforms where that does happen, we need to hang onto * the filter rule just in case someone decides to remove or flush it * in the meantime. */ if (fr != NULL) { MUTEX_ENTER(&fr->fr_lock); fr->fr_ref++; MUTEX_EXIT(&fr->fr_lock); } RWLOCK_EXIT(&softc->ipf_mutex); #endif if ((pass & FR_RETMASK) != 0) { /* * Should we return an ICMP packet to indicate error * status passing through the packet filter ? * WARNING: ICMP error packets AND TCP RST packets should * ONLY be sent in repsonse to incoming packets. Sending * them in response to outbound packets can result in a * panic on some operating systems. */ if (!out) { if (pass & FR_RETICMP) { int dst; if ((pass & FR_RETMASK) == FR_FAKEICMP) dst = 1; else dst = 0; (void) ipf_send_icmp_err(ICMP_UNREACH, fin, dst); LBUMP(ipf_stats[0].fr_ret); } else if (((pass & FR_RETMASK) == FR_RETRST) && !(fin->fin_flx & FI_SHORT)) { if (((fin->fin_flx & FI_OOW) != 0) || (ipf_send_reset(fin) == 0)) { LBUMP(ipf_stats[1].fr_ret); } } /* * When using return-* with auth rules, the auth code * takes over disposing of this packet. */ if (FR_ISAUTH(pass) && (fin->fin_m != NULL)) { DT1(frb_authcapture, fr_info_t *, fin); fin->fin_m = *fin->fin_mp = NULL; fin->fin_reason = FRB_AUTHCAPTURE; m = NULL; } } else { if (pass & FR_RETRST) { fin->fin_error = ECONNRESET; } } } /* * After the above so that ICMP unreachables and TCP RSTs get * created properly. */ if (FR_ISBLOCK(pass) && (fin->fin_flx & FI_NEWNAT)) ipf_nat_uncreate(fin); /* * If we didn't drop off the bottom of the list of rules (and thus * the 'current' rule fr is not NULL), then we may have some extra * instructions about what to do with a packet. * Once we're finished return to our caller, freeing the packet if * we are dropping it. */ if (fr != NULL) { frdest_t *fdp; /* * Generate a duplicated packet first because ipf_fastroute * can lead to fin_m being free'd... not good. */ fdp = fin->fin_dif; if ((fdp != NULL) && (fdp->fd_ptr != NULL) && (fdp->fd_ptr != (void *)-1)) { mc = M_COPY(fin->fin_m); if (mc != NULL) ipf_fastroute(mc, &mc, fin, fdp); } fdp = fin->fin_tif; if (!out && (pass & FR_FASTROUTE)) { /* * For fastroute rule, no destination interface defined * so pass NULL as the frdest_t parameter */ (void) ipf_fastroute(fin->fin_m, mp, fin, NULL); m = *mp = NULL; } else if ((fdp != NULL) && (fdp->fd_ptr != NULL) && (fdp->fd_ptr != (struct ifnet *)-1)) { /* this is for to rules: */ ipf_fastroute(fin->fin_m, mp, fin, fdp); m = *mp = NULL; } #if defined(FASTROUTE_RECURSION) (void) ipf_derefrule(softc, &fr); #endif } #if !defined(FASTROUTE_RECURSION) RWLOCK_EXIT(&softc->ipf_mutex); #endif finished: if (!FR_ISPASS(pass)) { LBUMP(ipf_stats[out].fr_block); if (*mp != NULL) { #ifdef _KERNEL FREE_MB_T(*mp); #endif m = *mp = NULL; } } else { LBUMP(ipf_stats[out].fr_pass); } SPL_X(s); if (fin->fin_m == NULL && fin->fin_flx & FI_BAD && fin->fin_reason == FRB_PULLUP) { /* m_pullup() has freed the mbuf */ LBUMP(ipf_stats[out].fr_blocked[fin->fin_reason]); return (-1); } #ifdef _KERNEL if (FR_ISPASS(pass)) return (0); LBUMP(ipf_stats[out].fr_blocked[fin->fin_reason]); return (fin->fin_error); #else /* _KERNEL */ if (*mp != NULL) (*mp)->mb_ifp = fin->fin_ifp; blockreason = fin->fin_reason; FR_VERBOSE(("fin_flx %#x pass %#x ", fin->fin_flx, pass)); /*if ((pass & FR_CMDMASK) == (softc->ipf_pass & FR_CMDMASK))*/ if ((pass & FR_NOMATCH) != 0) return (1); if ((pass & FR_RETMASK) != 0) switch (pass & FR_RETMASK) { case FR_RETRST : return (3); case FR_RETICMP : return (4); case FR_FAKEICMP : return (5); } switch (pass & FR_CMDMASK) { case FR_PASS : return (0); case FR_BLOCK : return (-1); case FR_AUTH : return (-2); case FR_ACCOUNT : return (-3); case FR_PREAUTH : return (-4); } return (2); #endif /* _KERNEL */ } #ifdef IPFILTER_LOG /* ------------------------------------------------------------------------ */ /* Function: ipf_dolog */ /* Returns: frentry_t* - returns contents of fin_fr (no change made) */ /* Parameters: fin(I) - pointer to packet information */ /* passp(IO) - pointer to current/new filter decision (unused) */ /* */ /* Checks flags set to see how a packet should be logged, if it is to be */ /* logged. Adjust statistics based on its success or not. */ /* ------------------------------------------------------------------------ */ frentry_t * ipf_dolog(fr_info_t *fin, u_32_t *passp) { ipf_main_softc_t *softc = fin->fin_main_soft; u_32_t pass; int out; out = fin->fin_out; pass = *passp; if ((softc->ipf_flags & FF_LOGNOMATCH) && (pass & FR_NOMATCH)) { pass |= FF_LOGNOMATCH; LBUMPD(ipf_stats[out], fr_npkl); goto logit; } else if (((pass & FR_LOGMASK) == FR_LOGP) || (FR_ISPASS(pass) && (softc->ipf_flags & FF_LOGPASS))) { if ((pass & FR_LOGMASK) != FR_LOGP) pass |= FF_LOGPASS; LBUMPD(ipf_stats[out], fr_ppkl); goto logit; } else if (((pass & FR_LOGMASK) == FR_LOGB) || (FR_ISBLOCK(pass) && (softc->ipf_flags & FF_LOGBLOCK))) { if ((pass & FR_LOGMASK) != FR_LOGB) pass |= FF_LOGBLOCK; LBUMPD(ipf_stats[out], fr_bpkl); logit: if (ipf_log_pkt(fin, pass) == -1) { /* * If the "or-block" option has been used then * block the packet if we failed to log it. */ if ((pass & FR_LOGORBLOCK) && FR_ISPASS(pass)) { DT1(frb_logfail2, u_int, pass); pass &= ~FR_CMDMASK; pass |= FR_BLOCK; fin->fin_reason = FRB_LOGFAIL2; } } *passp = pass; } return (fin->fin_fr); } #endif /* IPFILTER_LOG */ /* ------------------------------------------------------------------------ */ /* Function: ipf_cksum */ /* Returns: u_short - IP header checksum */ /* Parameters: addr(I) - pointer to start of buffer to checksum */ /* len(I) - length of buffer in bytes */ /* */ /* Calculate the two's complement 16 bit checksum of the buffer passed. */ /* */ /* N.B.: addr should be 16bit aligned. */ /* ------------------------------------------------------------------------ */ u_short ipf_cksum(u_short *addr, int len) { u_32_t sum = 0; for (sum = 0; len > 1; len -= 2) sum += *addr++; /* mop up an odd byte, if necessary */ if (len == 1) sum += *(u_char *)addr; /* * add back carry outs from top 16 bits to low 16 bits */ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ return (u_short)(~sum); } /* ------------------------------------------------------------------------ */ /* Function: fr_cksum */ /* Returns: u_short - layer 4 checksum */ /* Parameters: fin(I) - pointer to packet information */ /* ip(I) - pointer to IP header */ /* l4proto(I) - protocol to caclulate checksum for */ /* l4hdr(I) - pointer to layer 4 header */ /* */ /* Calculates the TCP checksum for the packet held in "m", using the data */ /* in the IP header "ip" to seed it. */ /* */ /* NB: This function assumes we've pullup'd enough for all of the IP header */ /* and the TCP header. We also assume that data blocks aren't allocated in */ /* odd sizes. */ /* */ /* Expects ip_len and ip_off to be in network byte order when called. */ /* ------------------------------------------------------------------------ */ u_short fr_cksum(fr_info_t *fin, ip_t *ip, int l4proto, void *l4hdr) { u_short *sp, slen, sumsave, *csump; u_int sum, sum2; int hlen; int off; #ifdef USE_INET6 ip6_t *ip6; #endif csump = NULL; sumsave = 0; sp = NULL; slen = 0; hlen = 0; sum = 0; sum = htons((u_short)l4proto); /* * Add up IP Header portion */ #ifdef USE_INET6 if (IP_V(ip) == 4) { #endif hlen = IP_HL(ip) << 2; off = hlen; sp = (u_short *)&ip->ip_src; sum += *sp++; /* ip_src */ sum += *sp++; sum += *sp++; /* ip_dst */ sum += *sp++; slen = fin->fin_plen - off; sum += htons(slen); #ifdef USE_INET6 } else if (IP_V(ip) == 6) { mb_t *m; m = fin->fin_m; ip6 = (ip6_t *)ip; off = ((caddr_t)ip6 - m->m_data) + sizeof(struct ip6_hdr); int len = ntohs(ip6->ip6_plen) - (off - sizeof(*ip6)); return (ipf_pcksum6(m, ip6, off, len)); } else { return (0xffff); } #endif switch (l4proto) { case IPPROTO_UDP : csump = &((udphdr_t *)l4hdr)->uh_sum; break; case IPPROTO_TCP : csump = &((tcphdr_t *)l4hdr)->th_sum; break; case IPPROTO_ICMP : csump = &((icmphdr_t *)l4hdr)->icmp_cksum; sum = 0; /* Pseudo-checksum is not included */ break; #ifdef USE_INET6 case IPPROTO_ICMPV6 : csump = &((struct icmp6_hdr *)l4hdr)->icmp6_cksum; break; #endif default : break; } if (csump != NULL) { sumsave = *csump; *csump = 0; } sum2 = ipf_pcksum(fin, off, sum); if (csump != NULL) *csump = sumsave; return (sum2); } /* ------------------------------------------------------------------------ */ /* Function: ipf_findgroup */ /* Returns: frgroup_t * - NULL = group not found, else pointer to group */ /* Parameters: softc(I) - pointer to soft context main structure */ /* group(I) - group name to search for */ /* unit(I) - device to which this group belongs */ /* set(I) - which set of rules (inactive/inactive) this is */ /* fgpp(O) - pointer to place to store pointer to the pointer */ /* to where to add the next (last) group or where */ /* to delete group from. */ /* */ /* Search amongst the defined groups for a particular group number. */ /* ------------------------------------------------------------------------ */ frgroup_t * ipf_findgroup(ipf_main_softc_t *softc, char *group, minor_t unit, int set, frgroup_t ***fgpp) { frgroup_t *fg, **fgp; /* * Which list of groups to search in is dependent on which list of * rules are being operated on. */ fgp = &softc->ipf_groups[unit][set]; while ((fg = *fgp) != NULL) { if (strncmp(group, fg->fg_name, FR_GROUPLEN) == 0) break; else fgp = &fg->fg_next; } if (fgpp != NULL) *fgpp = fgp; return (fg); } /* ------------------------------------------------------------------------ */ /* Function: ipf_group_add */ /* Returns: frgroup_t * - NULL == did not create group, */ /* != NULL == pointer to the group */ /* Parameters: softc(I) - pointer to soft context main structure */ /* num(I) - group number to add */ /* head(I) - rule pointer that is using this as the head */ /* flags(I) - rule flags which describe the type of rule it is */ /* unit(I) - device to which this group will belong to */ /* set(I) - which set of rules (inactive/inactive) this is */ /* Write Locks: ipf_mutex */ /* */ /* Add a new group head, or if it already exists, increase the reference */ /* count to it. */ /* ------------------------------------------------------------------------ */ frgroup_t * ipf_group_add(ipf_main_softc_t *softc, char *group, void *head, u_32_t flags, minor_t unit, int set) { frgroup_t *fg, **fgp; u_32_t gflags; if (group == NULL) return (NULL); if (unit == IPL_LOGIPF && *group == '\0') return (NULL); fgp = NULL; gflags = flags & FR_INOUT; fg = ipf_findgroup(softc, group, unit, set, &fgp); if (fg != NULL) { if (fg->fg_head == NULL && head != NULL) fg->fg_head = head; if (fg->fg_flags == 0) fg->fg_flags = gflags; else if (gflags != fg->fg_flags) return (NULL); fg->fg_ref++; return (fg); } KMALLOC(fg, frgroup_t *); if (fg != NULL) { fg->fg_head = head; fg->fg_start = NULL; fg->fg_next = *fgp; bcopy(group, fg->fg_name, strlen(group) + 1); fg->fg_flags = gflags; fg->fg_ref = 1; fg->fg_set = &softc->ipf_groups[unit][set]; *fgp = fg; } return (fg); } /* ------------------------------------------------------------------------ */ /* Function: ipf_group_del */ /* Returns: int - number of rules deleted */ /* Parameters: softc(I) - pointer to soft context main structure */ /* group(I) - group name to delete */ /* fr(I) - filter rule from which group is referenced */ /* Write Locks: ipf_mutex */ /* */ /* This function is called whenever a reference to a group is to be dropped */ /* and thus its reference count needs to be lowered and the group free'd if */ /* the reference count reaches zero. Passing in fr is really for the sole */ /* purpose of knowing when the head rule is being deleted. */ /* ------------------------------------------------------------------------ */ void ipf_group_del(ipf_main_softc_t *softc, frgroup_t *group, frentry_t *fr) { if (group->fg_head == fr) group->fg_head = NULL; group->fg_ref--; if ((group->fg_ref == 0) && (group->fg_start == NULL)) ipf_group_free(group); } /* ------------------------------------------------------------------------ */ /* Function: ipf_group_free */ /* Returns: Nil */ /* Parameters: group(I) - pointer to filter rule group */ /* */ /* Remove the group from the list of groups and free it. */ /* ------------------------------------------------------------------------ */ static void ipf_group_free(frgroup_t *group) { frgroup_t **gp; for (gp = group->fg_set; *gp != NULL; gp = &(*gp)->fg_next) { if (*gp == group) { *gp = group->fg_next; break; } } KFREE(group); } /* ------------------------------------------------------------------------ */ /* Function: ipf_group_flush */ /* Returns: int - number of rules flush from group */ /* Parameters: softc(I) - pointer to soft context main structure */ /* Parameters: group(I) - pointer to filter rule group */ /* */ /* Remove all of the rules that currently are listed under the given group. */ /* ------------------------------------------------------------------------ */ static int ipf_group_flush(ipf_main_softc_t *softc, frgroup_t *group) { int gone = 0; (void) ipf_flushlist(softc, &gone, &group->fg_start); return (gone); } /* ------------------------------------------------------------------------ */ /* Function: ipf_getrulen */ /* Returns: frentry_t * - NULL == not found, else pointer to rule n */ /* Parameters: softc(I) - pointer to soft context main structure */ /* Parameters: unit(I) - device for which to count the rule's number */ /* flags(I) - which set of rules to find the rule in */ /* group(I) - group name */ /* n(I) - rule number to find */ /* */ /* Find rule # n in group # g and return a pointer to it. Return NULl if */ /* group # g doesn't exist or there are less than n rules in the group. */ /* ------------------------------------------------------------------------ */ frentry_t * ipf_getrulen(ipf_main_softc_t *softc, int unit, char *group, u_32_t n) { frentry_t *fr; frgroup_t *fg; fg = ipf_findgroup(softc, group, unit, softc->ipf_active, NULL); if (fg == NULL) return (NULL); for (fr = fg->fg_start; fr && n; fr = fr->fr_next, n--) ; if (n != 0) return (NULL); return (fr); } /* ------------------------------------------------------------------------ */ /* Function: ipf_flushlist */ /* Returns: int - >= 0 - number of flushed rules */ /* Parameters: softc(I) - pointer to soft context main structure */ /* nfreedp(O) - pointer to int where flush count is stored */ /* listp(I) - pointer to list to flush pointer */ /* Write Locks: ipf_mutex */ /* */ /* Recursively flush rules from the list, descending groups as they are */ /* encountered. if a rule is the head of a group and it has lost all its */ /* group members, then also delete the group reference. nfreedp is needed */ /* to store the accumulating count of rules removed, whereas the returned */ /* value is just the number removed from the current list. The latter is */ /* needed to correctly adjust reference counts on rules that define groups. */ /* */ /* NOTE: Rules not loaded from user space cannot be flushed. */ /* ------------------------------------------------------------------------ */ static int ipf_flushlist(ipf_main_softc_t *softc, int *nfreedp, frentry_t **listp) { int freed = 0; frentry_t *fp; while ((fp = *listp) != NULL) { if ((fp->fr_type & FR_T_BUILTIN) || !(fp->fr_flags & FR_COPIED)) { listp = &fp->fr_next; continue; } *listp = fp->fr_next; if (fp->fr_next != NULL) fp->fr_next->fr_pnext = fp->fr_pnext; fp->fr_pnext = NULL; if (fp->fr_grphead != NULL) { freed += ipf_group_flush(softc, fp->fr_grphead); fp->fr_names[fp->fr_grhead] = '\0'; } if (fp->fr_icmpgrp != NULL) { freed += ipf_group_flush(softc, fp->fr_icmpgrp); fp->fr_names[fp->fr_icmphead] = '\0'; } if (fp->fr_srctrack.ht_max_nodes) ipf_rb_ht_flush(&fp->fr_srctrack); fp->fr_next = NULL; ASSERT(fp->fr_ref > 0); if (ipf_derefrule(softc, &fp) == 0) freed++; } *nfreedp += freed; return (freed); } /* ------------------------------------------------------------------------ */ /* Function: ipf_flush */ /* Returns: int - >= 0 - number of flushed rules */ /* Parameters: softc(I) - pointer to soft context main structure */ /* unit(I) - device for which to flush rules */ /* flags(I) - which set of rules to flush */ /* */ /* Calls flushlist() for all filter rules (accounting, firewall - both IPv4 */ /* and IPv6) as defined by the value of flags. */ /* ------------------------------------------------------------------------ */ int ipf_flush(ipf_main_softc_t *softc, minor_t unit, int flags) { int flushed = 0, set; WRITE_ENTER(&softc->ipf_mutex); set = softc->ipf_active; if ((flags & FR_INACTIVE) == FR_INACTIVE) set = 1 - set; if (flags & FR_OUTQUE) { ipf_flushlist(softc, &flushed, &softc->ipf_rules[1][set]); ipf_flushlist(softc, &flushed, &softc->ipf_acct[1][set]); } if (flags & FR_INQUE) { ipf_flushlist(softc, &flushed, &softc->ipf_rules[0][set]); ipf_flushlist(softc, &flushed, &softc->ipf_acct[0][set]); } flushed += ipf_flush_groups(softc, &softc->ipf_groups[unit][set], flags & (FR_INQUE|FR_OUTQUE)); RWLOCK_EXIT(&softc->ipf_mutex); if (unit == IPL_LOGIPF) { int tmp; tmp = ipf_flush(softc, IPL_LOGCOUNT, flags); if (tmp >= 0) flushed += tmp; } return (flushed); } /* ------------------------------------------------------------------------ */ /* Function: ipf_flush_groups */ /* Returns: int - >= 0 - number of flushed rules */ /* Parameters: softc(I) - soft context pointerto work with */ /* grhead(I) - pointer to the start of the group list to flush */ /* flags(I) - which set of rules to flush */ /* */ /* Walk through all of the groups under the given group head and remove all */ /* of those that match the flags passed in. The for loop here is bit more */ /* complicated than usual because the removal of a rule with ipf_derefrule */ /* may end up removing not only the structure pointed to by "fg" but also */ /* what is fg_next and fg_next after that. So if a filter rule is actually */ /* removed from the group then it is necessary to start again. */ /* ------------------------------------------------------------------------ */ static int ipf_flush_groups(ipf_main_softc_t *softc, frgroup_t **grhead, int flags) { frentry_t *fr, **frp; frgroup_t *fg, **fgp; int flushed = 0; int removed = 0; for (fgp = grhead; (fg = *fgp) != NULL; ) { while ((fg != NULL) && ((fg->fg_flags & flags) == 0)) fg = fg->fg_next; if (fg == NULL) break; removed = 0; frp = &fg->fg_start; while ((removed == 0) && ((fr = *frp) != NULL)) { if ((fr->fr_flags & flags) == 0) { frp = &fr->fr_next; } else { if (fr->fr_next != NULL) fr->fr_next->fr_pnext = fr->fr_pnext; *frp = fr->fr_next; fr->fr_pnext = NULL; fr->fr_next = NULL; (void) ipf_derefrule(softc, &fr); flushed++; removed++; } } if (removed == 0) fgp = &fg->fg_next; } return (flushed); } /* ------------------------------------------------------------------------ */ /* Function: memstr */ /* Returns: char * - NULL if failed, != NULL pointer to matching bytes */ /* Parameters: src(I) - pointer to byte sequence to match */ /* dst(I) - pointer to byte sequence to search */ /* slen(I) - match length */ /* dlen(I) - length available to search in */ /* */ /* Search dst for a sequence of bytes matching those at src and extend for */ /* slen bytes. */ /* ------------------------------------------------------------------------ */ char * memstr(const char *src, char *dst, size_t slen, size_t dlen) { char *s = NULL; while (dlen >= slen) { if (bcmp(src, dst, slen) == 0) { s = dst; break; } dst++; dlen--; } return (s); } /* ------------------------------------------------------------------------ */ /* Function: ipf_fixskip */ /* Returns: Nil */ /* Parameters: listp(IO) - pointer to start of list with skip rule */ /* rp(I) - rule added/removed with skip in it. */ /* addremove(I) - adjustment (-1/+1) to make to skip count, */ /* depending on whether a rule was just added */ /* or removed. */ /* */ /* Adjust all the rules in a list which would have skip'd past the position */ /* where we are inserting to skip to the right place given the change. */ /* ------------------------------------------------------------------------ */ void ipf_fixskip(frentry_t **listp, frentry_t *rp, int addremove) { int rules, rn; frentry_t *fp; rules = 0; for (fp = *listp; (fp != NULL) && (fp != rp); fp = fp->fr_next) rules++; if (fp == NULL) return; for (rn = 0, fp = *listp; fp && (fp != rp); fp = fp->fr_next, rn++) if (FR_ISSKIP(fp->fr_flags) && (rn + fp->fr_arg >= rules)) fp->fr_arg += addremove; } #ifdef _KERNEL /* ------------------------------------------------------------------------ */ /* Function: count4bits */ /* Returns: int - >= 0 - number of consecutive bits in input */ /* Parameters: ip(I) - 32bit IP address */ /* */ /* IPv4 ONLY */ /* count consecutive 1's in bit mask. If the mask generated by counting */ /* consecutive 1's is different to that passed, return -1, else return # */ /* of bits. */ /* ------------------------------------------------------------------------ */ int count4bits(u_32_t ip) { u_32_t ipn; int cnt = 0, i, j; ip = ipn = ntohl(ip); for (i = 32; i; i--, ipn *= 2) if (ipn & 0x80000000) cnt++; else break; ipn = 0; for (i = 32, j = cnt; i; i--, j--) { ipn *= 2; if (j > 0) ipn++; } if (ipn == ip) return (cnt); return (-1); } /* ------------------------------------------------------------------------ */ /* Function: count6bits */ /* Returns: int - >= 0 - number of consecutive bits in input */ /* Parameters: msk(I) - pointer to start of IPv6 bitmask */ /* */ /* IPv6 ONLY */ /* count consecutive 1's in bit mask. */ /* ------------------------------------------------------------------------ */ # ifdef USE_INET6 int count6bits(u_32_t *msk) { int i = 0, k; u_32_t j; for (k = 3; k >= 0; k--) if (msk[k] == 0xffffffff) i += 32; else { for (j = msk[k]; j; j <<= 1) if (j & 0x80000000) i++; } return (i); } # endif #endif /* _KERNEL */ /* ------------------------------------------------------------------------ */ /* Function: ipf_synclist */ /* Returns: int - 0 = no failures, else indication of first failure */ /* Parameters: fr(I) - start of filter list to sync interface names for */ /* ifp(I) - interface pointer for limiting sync lookups */ /* Write Locks: ipf_mutex */ /* */ /* Walk through a list of filter rules and resolve any interface names into */ /* pointers. Where dynamic addresses are used, also update the IP address */ /* used in the rule. The interface pointer is used to limit the lookups to */ /* a specific set of matching names if it is non-NULL. */ /* Errors can occur when resolving the destination name of to/dup-to fields */ /* when the name points to a pool and that pool doest not exist. If this */ /* does happen then it is necessary to check if there are any lookup refs */ /* that need to be dropped before returning with an error. */ /* ------------------------------------------------------------------------ */ static int ipf_synclist(ipf_main_softc_t *softc, frentry_t *fr, void *ifp) { frentry_t *frt, *start = fr; frdest_t *fdp; char *name; int error; void *ifa; int v, i; error = 0; for (; fr; fr = fr->fr_next) { if (fr->fr_family == AF_INET) v = 4; else if (fr->fr_family == AF_INET6) v = 6; else v = 0; /* * Lookup all the interface names that are part of the rule. */ for (i = 0; i < FR_NUM(fr->fr_ifas); i++) { if ((ifp != NULL) && (fr->fr_ifas[i] != ifp)) continue; if (fr->fr_ifnames[i] == -1) continue; name = FR_NAME(fr, fr_ifnames[i]); fr->fr_ifas[i] = ipf_resolvenic(softc, name, v); } if ((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) { if (fr->fr_satype != FRI_NORMAL && fr->fr_satype != FRI_LOOKUP) { ifa = ipf_resolvenic(softc, fr->fr_names + fr->fr_sifpidx, v); ipf_ifpaddr(softc, v, fr->fr_satype, ifa, &fr->fr_src6, &fr->fr_smsk6); } if (fr->fr_datype != FRI_NORMAL && fr->fr_datype != FRI_LOOKUP) { ifa = ipf_resolvenic(softc, fr->fr_names + fr->fr_sifpidx, v); ipf_ifpaddr(softc, v, fr->fr_datype, ifa, &fr->fr_dst6, &fr->fr_dmsk6); } } fdp = &fr->fr_tifs[0]; if ((ifp == NULL) || (fdp->fd_ptr == ifp)) { error = ipf_resolvedest(softc, fr->fr_names, fdp, v); if (error != 0) goto unwind; } fdp = &fr->fr_tifs[1]; if ((ifp == NULL) || (fdp->fd_ptr == ifp)) { error = ipf_resolvedest(softc, fr->fr_names, fdp, v); if (error != 0) goto unwind; } fdp = &fr->fr_dif; if ((ifp == NULL) || (fdp->fd_ptr == ifp)) { error = ipf_resolvedest(softc, fr->fr_names, fdp, v); if (error != 0) goto unwind; } if (((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) && (fr->fr_satype == FRI_LOOKUP) && (fr->fr_srcptr == NULL)) { fr->fr_srcptr = ipf_lookup_res_num(softc, fr->fr_srctype, IPL_LOGIPF, fr->fr_srcnum, &fr->fr_srcfunc); } if (((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) && (fr->fr_datype == FRI_LOOKUP) && (fr->fr_dstptr == NULL)) { fr->fr_dstptr = ipf_lookup_res_num(softc, fr->fr_dsttype, IPL_LOGIPF, fr->fr_dstnum, &fr->fr_dstfunc); } } return (0); unwind: for (frt = start; frt != fr; fr = fr->fr_next) {