makefs/contrib/code/mirmake/dist/contrib/fgetln.c010064400000000000000000000033161244537337200173150ustar00/*- * Copyright (c) 2007, 2009 * Thorsten Glaser * * Provided that these terms and disclaimer and all copyright notices * are retained or reproduced in an accompanying document, permission * is granted to deal in this work without restriction, including un- * limited rights to use, publicly perform, distribute, sell, modify, * merge, give away, or sublicence. * * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to * the utmost extent permitted by applicable law, neither express nor * implied; without malicious intent or gross negligence. In no event * may a licensor, author or contributor be held liable for indirect, * direct, other damage, loss, or other issues arising in any way out * of dealing in the work, even if advised of the possibility of such * damage or existence of a defect, except proven that it results out * of said person's immediate fault when using the work as intended. *- * fgetln() wrapper for operating systems with getline() – glibc */ #undef _GNU_SOURCE #define _GNU_SOURCE /* for getline() */ #include #include #include __RCSID("$MirOS: contrib/code/mirmake/dist/contrib/fgetln.c,v 1.7 2014/12/20 22:23:29 tg Exp $"); #if defined(__GLIBC__) #if !defined(_MIRMAKE_H) || !defined(_MIRMAKE_DEFNS) char *fgetln(FILE *, size_t *); #endif char * fgetln(FILE *stream, size_t *len) { static char *lb = NULL; static size_t lbsz = 0; if ((*len = getline(&lb, &lbsz, stream)) != (size_t)-1) /* getdelim ensures *len is not 0 here */ return (lb); /* not required by manpage, but reference implementation does this */ *len = 0; /* not required to zero lb or lbsz: getdelim manages it */ return (NULL); } #endif makefs/src/include/vis.h010064400000000000000000000102431341415747100124460ustar00/** $MirOS: src/include/vis.h,v 1.5 2019/01/05 16:45:18 tg Exp $ */ /* $OpenBSD: vis.h,v 1.11 2005/08/09 19:38:31 millert Exp $ */ /* $NetBSD: vis.h,v 1.16 2005/09/13 01:44:32 christos Exp $ */ /*- * Copyright © 2013, 2018, 2019 * mirabilos * Copyright (c) 1990, 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. * * @(#)vis.h 8.1 (Berkeley) 6/2/93 */ #ifndef _VIS_H_ #define _VIS_H_ /* * to select alternate encoding format */ #define VIS_OCTAL 0x01 /* use octal \ddd format */ #define VIS_CSTYLE 0x02 /* use \[nrft0..] where appropriate */ /* * to alter set of characters encoded (default is to encode all * non-graphic except space, tab, and newline). */ #define VIS_SP 0x04 /* also encode space */ #define VIS_TAB 0x08 /* also encode tab */ #define VIS_NL 0x10 /* also encode newline */ #define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL) #define VIS_SAFE 0x20 /* only encode "unsafe" characters */ /* * other */ #define VIS_NOSLASH 0x40 /* inhibit printing '\' */ #define VIS_HTTPSTYLE 0x80 /* http-style escape % HEX HEX */ #define VIS_GLOB 0x100 /* encode glob(3) magics and '#' */ /* supported by strnvis()/strnsvis() only; MirBSD extension */ #define VIS_UTF8 0x4000 /* pass non-control non-ASCII chars as-is */ /* * unvis return codes */ #define UNVIS_VALID 1 /* character valid */ #define UNVIS_VALIDPUSH 2 /* character valid, push back passed char */ #define UNVIS_NOCHAR 3 /* valid sequence, no character produced */ #define UNVIS_SYNBAD -1 /* unrecognized escape sequence */ #define UNVIS_ERROR -2 /* decoder in unknown state (unrecoverable) */ /* * unvis flags */ #define UNVIS_END 1 /* no more characters */ #include #if defined(__GNUC__) && (defined(__OpenBSD__) || defined(__MirBSD__)) #define _VIS_H_bounded(...) __attribute__((__bounded__(__VA_ARGS__))) #else #define _VIS_H_bounded(...) /* nothing */ #endif __BEGIN_DECLS char *vis(char *, int, int, int); char *svis(char *, int, int, int, const char *); int strvis(char *, const char *, int); int strsvis(char *, const char *, int, const char *); int strnvis(char *, const char *, size_t, int) _VIS_H_bounded(__string__, 1, 3); int strnsvis(char *, const char *, size_t, int, const char *) _VIS_H_bounded(__string__, 1, 3); int strvisx(char *, const char *, size_t, int) _VIS_H_bounded(__string__, 1, 3); int strsvisx(char *, const char *, size_t, int, const char *) _VIS_H_bounded(__string__, 1, 3); int strunvis(char *, const char *); int strunvisx(char *, const char *, int) _VIS_H_bounded(__string__, 1, 3); int unvis(char *, char, int *, int); ssize_t strnunvis(char *, const char *, size_t) _VIS_H_bounded(__string__, 1, 3); __END_DECLS #undef _VIS_H_bounded #endif /* !_VIS_H_ */ makefs/src/kern/c/strlfun.c010064400000000000000000000126201341415673100130720ustar00#if 0 /* comment in gmake; next line ignored by gcc */ ifeq (0,gmake ignores from here) #endif /*- * Copyright (c) 2006, 2008, 2011 * mirabilos * * Provided that these terms and disclaimer and all copyright notices * are retained or reproduced in an accompanying document, permission * is granted to deal in this work without restriction, including un- * limited rights to use, publicly perform, distribute, sell, modify, * merge, give away, or sublicence. * * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to * the utmost extent permitted by applicable law, neither express nor * implied; without malicious intent or gross negligence. In no event * may a licensor, author or contributor be held liable for indirect, * direct, other damage, loss, or other issues arising in any way out * of dealing in the work, even if advised of the possibility of such * damage or existence of a defect, except proven that it results out * of said person's immediate fault when using the work as intended. *- * The original implementations of strlcpy(3) and strlcat(3) are from * Todd C. Miller; the licence is reproduced below. However, this ap- * plies only to the strlcpy(3) portion of the code, as Thorsten Gla- * ser write the following strlcat(3) implementation according to the * spec. Both functions below have been optimised according to sugge- * stions from Bodo Eggert. mirabilos merged the code with strxfrm(3) * for UTF-8-only systems and the wide character variants wcslcat(3), * wcslcpy(3), and wcsxfrm(3). */ #include #ifndef OUTSIDE_OF_LIBKERN #include #endif #ifndef __RCSID #define __RCSID(x) static const char __rcsid[] = x #endif __RCSID("$MirOS: src/kern/c/strlfun.c,v 1.7 2019/01/05 16:39:26 tg Exp $"); #ifdef WIDEC #ifdef OUTSIDE_OF_LIBKERN #ifdef __WCHAR_TYPE__ typedef __WCHAR_TYPE__ wchar_t; #else #include #endif #endif /* wide character string functions */ #define NUL L'\0' #define char_t wchar_t #define fn_len wcslen #define fn_cat wcslcat #define fn_cpy wcslcpy #else /* (multibyte) string functions */ #define NUL '\0' #define char_t char #define fn_len strlen #define fn_cat strlcat #define fn_cpy strlcpy #endif #ifdef L_strxfrm #define strlcpy strxfrm #define wcslcpy wcsxfrm #define L_strlcpy #endif #ifdef OUTSIDE_OF_LIBKERN extern size_t fn_len(const char_t *); #ifdef NEED_STRLFUN_PROTOS size_t fn_cat(char_t *, const char_t *, size_t); size_t fn_cpy(char_t *, const char_t *, size_t); #endif #endif #ifndef __predict_true #define __predict_true(exp) (exp) #define __predict_false(exp) (exp) #endif #ifdef L_strlcat /* * Appends src to string dst of size dlen (unlike strncat, dlen is the * full size of dst, not space left). At most dlen-1 characters * will be copied. Always NUL terminates (unless dlen <= strlen(dst)). * Returns strlen(src) + MIN(dlen, strlen(initial dst)), without the * trailing NUL byte counted. If retval >= dlen, truncation occurred. */ size_t fn_cat(char_t *dst, const char_t *src, size_t dlen) { size_t n = 0, slen; slen = fn_len(src); while (__predict_true(n + 1 < dlen && dst[n] != NUL)) ++n; if (__predict_false(dlen == 0 || dst[n] != NUL)) return (dlen + slen); while (__predict_true((slen > 0) && (n < (dlen - 1)))) { dst[n++] = *src++; --slen; } dst[n] = NUL; return (n + slen); } #endif #ifdef L_strlcpy /* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ /*- * Copyright (c) 1998 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless siz == 0). * Returns strlen(src); if retval >= siz, truncation occurred. */ size_t fn_cpy(char_t *dst, const char_t *src, size_t siz) { const char_t *s = src; if (__predict_false(siz == 0)) goto traverse_src; /* copy as many chars as will fit */ while (--siz && (*dst++ = *s++)) ; /* not enough room in dst */ if (__predict_false(siz == 0)) { /* safe to NUL-terminate dst since we copied <= siz-1 chars */ *dst = NUL; traverse_src: /* traverse rest of src */ while (*s++) ; } /* count does not include NUL */ return (s - src - 1); } #endif #if 0 /* gcc ignored from here; gmake stops ignoring */ endif USE_WIDEC?= 1 LIB= libstrlfun.a OBJS= strlcpy.o strlcat.o ifeq (1,$(strip $(USE_WIDEC))) OBJS+= wcslcpy.o wcslcat.o endif DEFS= -DOUTSIDE_OF_LIBKERN DEFS_strlcpy.o= -DL_strlcpy DEFS_strlcat.o= -DL_strlcat DEFS_wcslcpy.o= -DL_strlcpy -DWIDEC DEFS_wcslcat.o= -DL_strlcat -DWIDEC all: $(LIB) $(LIB): $(OBJS) ar rc $(LIB) $(OBJS) -ranlib $(LIB) $(OBJS): strlfun.c $(CC) $(CFLAGS) $(CPPFLAGS) $(DEFS) $(DEFS_$@) -c -o $@ strlfun.c #endif /* EOF for gmake and gcc */ makefs/src/lib/libc/gen/setmode.c010064400000000000000000000261051341415530600141040ustar00/* $OpenBSD: setmode.c,v 1.17 2005/08/08 08:05:34 espie Exp $ */ /* $NetBSD: setmode.c,v 1.15 1997/02/07 22:21:06 christos Exp $ */ /* * Copyright (c) 1989, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Dave Borman at Cray Research, 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. */ #include #include #include #include #include #include #include #include #ifdef SETMODE_DEBUG #include #endif __SCCSID("@(#)setmode.c 8.2 (Berkeley) 3/25/94"); __RCSID("$MirOS: src/lib/libc/gen/setmode.c,v 1.17 2019/01/05 16:26:19 tg Exp $"); #ifdef GNUPORT mode_t getmode(const void *, mode_t); void *setmode(const char *); #endif #define SET_LEN 6 /* initial # of bitcmd struct to malloc */ #define SET_LEN_INCR 4 /* # of bitcmd structs to add as needed */ typedef struct bitcmd { mode_t bits; char cmd; char cmd2; } BITCMD; #define CMD2_CLR 0x01 #define CMD2_SET 0x02 #define CMD2_GBITS 0x04 #define CMD2_OBITS 0x08 #define CMD2_UBITS 0x10 static BITCMD *addcmd(BITCMD *, int, int, int, u_int); static void compress_mode(BITCMD *); #ifdef SETMODE_DEBUG static void dumpmode(BITCMD *); #endif /* * Given the old mode and an array of bitcmd structures, apply the operations * described in the bitcmd structures to the old mode, and return the new mode. * Note that there is no '=' command; a strict assignment is just a '-' (clear * bits) followed by a '+' (set bits). */ mode_t getmode(const void *bbox, mode_t omode) { const BITCMD *set; mode_t clrval, newmode, value; set = (const BITCMD *)bbox; newmode = omode; for (value = 0;; set++) switch(set->cmd) { /* * When copying the user, group or other bits around, we "know" * where the bits are in the mode so that we can do shifts to * copy them around. If we don't use shifts, it gets real * grundgy with lots of single bit checks and bit sets. */ case 'u': value = (newmode & S_IRWXU) >> 6; goto common; case 'g': value = (newmode & S_IRWXG) >> 3; goto common; case 'o': value = newmode & S_IRWXO; common: if (set->cmd2 & CMD2_CLR) { clrval = (set->cmd2 & CMD2_SET) ? S_IRWXO : value; if (set->cmd2 & CMD2_UBITS) newmode &= ~((clrval<<6) & set->bits); if (set->cmd2 & CMD2_GBITS) newmode &= ~((clrval<<3) & set->bits); if (set->cmd2 & CMD2_OBITS) newmode &= ~(clrval & set->bits); } if (set->cmd2 & CMD2_SET) { if (set->cmd2 & CMD2_UBITS) newmode |= (value<<6) & set->bits; if (set->cmd2 & CMD2_GBITS) newmode |= (value<<3) & set->bits; if (set->cmd2 & CMD2_OBITS) newmode |= value & set->bits; } break; case '+': newmode |= set->bits; break; case '-': newmode &= ~set->bits; break; case 'X': if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH)) newmode |= set->bits; break; case '\0': default: #ifdef SETMODE_DEBUG (void)printf("getmode:%04o -> %04o\n", omode, newmode); #endif return (newmode); } } #define notoktomul(a, b) ((a) && (b) && (SIZE_MAX / (a) < (b))) #define ADDCMD(a, b, c, d) \ if (set >= endset) { \ BITCMD *newset; \ setlen += SET_LEN_INCR; \ if (notoktomul(setlen, sizeof(BITCMD)) || \ (newset = realloc(saveset, setlen * \ sizeof(BITCMD))) == NULL) { \ free(saveset); \ return (NULL); \ } \ set = newset + (set - saveset); \ saveset = newset; \ endset = newset + (setlen - 2); \ } \ set = addcmd(set, (a), (b), (c), (d)) #define STANDARD_BITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) void * setmode(const char *p) { int perm, who; char op, *ep; BITCMD *set, *saveset, *endset; sigset_t signset, sigoset; mode_t mask; int equalopdone = 0, permXbits, setlen; u_long perml; if (!*p) return (NULL); /* * Get a copy of the mask for the permissions that are mask relative. * Flip the bits, we want what's not set. Since it's possible that * the caller is opening files inside a signal handler, protect them * as best we can. */ sigfillset(&signset); (void)sigprocmask(SIG_BLOCK, &signset, &sigoset); (void)umask(mask = umask(0)); mask = ~mask; (void)sigprocmask(SIG_SETMASK, &sigoset, NULL); setlen = SET_LEN + 2; if (notoktomul(setlen, sizeof(BITCMD)) || (set = malloc(setlen * sizeof(BITCMD))) == NULL) return (NULL); saveset = set; endset = set + (setlen - 2); /* * If an absolute number, get it and return; disallow non-octal digits * or illegal bits. */ if (isdigit((unsigned char)*p)) { perml = strtoul(p, &ep, 8); /* The test on perml will also catch overflow. */ if (*ep != '\0' || (perml & ~(STANDARD_BITS|S_ISTXT))) { free(saveset); errno = ERANGE; return (NULL); } perm = (mode_t)perml; ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask); set->cmd = 0; return (saveset); } /* * Build list of structures to set/clear/copy bits as described by * each clause of the symbolic mode. */ for (;;) { /* First, find out which bits might be modified. */ for (who = 0;; ++p) { switch (*p) { case 'a': who |= STANDARD_BITS; break; case 'u': who |= S_ISUID|S_IRWXU; break; case 'g': who |= S_ISGID|S_IRWXG; break; case 'o': who |= S_IRWXO; break; default: goto getop; } } getop: if ((op = *p++) != '+' && op != '-' && op != '=') { free(saveset); return (NULL); } if (op == '=') equalopdone = 0; who &= ~S_ISTXT; for (perm = 0, permXbits = 0;; ++p) { switch (*p) { case 'r': perm |= S_IRUSR|S_IRGRP|S_IROTH; break; case 's': /* * If specific bits where requested and * only "other" bits ignore set-id. */ if (who == 0 || (who & ~S_IRWXO)) perm |= S_ISUID|S_ISGID; break; case 't': /* * If specific bits where requested and * only "other" bits ignore sticky. */ if (who == 0 || (who & ~S_IRWXO)) { who |= S_ISTXT; perm |= S_ISTXT; } break; case 'w': perm |= S_IWUSR|S_IWGRP|S_IWOTH; break; case 'X': permXbits = S_IXUSR|S_IXGRP|S_IXOTH; break; case 'x': perm |= S_IXUSR|S_IXGRP|S_IXOTH; break; case 'u': case 'g': case 'o': /* * When ever we hit 'u', 'g', or 'o', we have * to flush out any partial mode that we have, * and then do the copying of the mode bits. */ if (perm) { ADDCMD(op, who, perm, mask); perm = 0; } if (op == '=') equalopdone = 1; if (op == '+' && permXbits) { ADDCMD('X', who, permXbits, mask); permXbits = 0; } ADDCMD(*p, who, op, mask); break; default: /* * Add any permissions that we haven't already * done. */ if (perm || (op == '=' && !equalopdone)) { if (op == '=') equalopdone = 1; ADDCMD(op, who, perm, mask); perm = 0; } if (permXbits) { ADDCMD('X', who, permXbits, mask); permXbits = 0; } goto apply; } } apply: if (!*p) break; if (*p != ',') goto getop; ++p; } set->cmd = 0; #ifdef SETMODE_DEBUG (void)printf("Before compress_mode()\n"); dumpmode(saveset); #endif compress_mode(saveset); #ifdef SETMODE_DEBUG (void)printf("After compress_mode()\n"); dumpmode(saveset); #endif return (saveset); } static BITCMD * addcmd(BITCMD *set, int op, int who, int oparg, u_int mask) { switch (op) { case '=': set->cmd = '-'; set->bits = who ? who : STANDARD_BITS; set++; op = '+'; /* FALLTHROUGH */ case '+': case '-': case 'X': set->cmd = op; set->bits = (who ? who : (int)mask) & oparg; break; case 'u': case 'g': case 'o': set->cmd = op; if (who) { set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) | ((who & S_IRGRP) ? CMD2_GBITS : 0) | ((who & S_IROTH) ? CMD2_OBITS : 0); set->bits = (mode_t)~0; } else { set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS; set->bits = mask; } if (oparg == '+') set->cmd2 |= CMD2_SET; else if (oparg == '-') set->cmd2 |= CMD2_CLR; else if (oparg == '=') set->cmd2 |= CMD2_SET|CMD2_CLR; break; } return (set + 1); } #ifdef SETMODE_DEBUG static void dumpmode(BITCMD *set) { for (; set->cmd; ++set) (void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n", set->cmd, set->bits, set->cmd2 ? " cmd2:" : "", set->cmd2 & CMD2_CLR ? " CLR" : "", set->cmd2 & CMD2_SET ? " SET" : "", set->cmd2 & CMD2_UBITS ? " UBITS" : "", set->cmd2 & CMD2_GBITS ? " GBITS" : "", set->cmd2 & CMD2_OBITS ? " OBITS" : ""); } #endif /* * Given an array of bitcmd structures, compress by compacting consecutive * '+', '-' and 'X' commands into at most 3 commands, one of each. The 'u', * 'g' and 'o' commands continue to be separate. They could probably be * compacted, but it's not worth the effort. */ static void compress_mode(BITCMD *set) { BITCMD *nset; int setbits, clrbits, Xbits, op; for (nset = set;;) { /* Copy over any 'u', 'g' and 'o' commands. */ while ((op = nset->cmd) != '+' && op != '-' && op != 'X') { *set++ = *nset++; if (!op) return; } for (setbits = clrbits = Xbits = 0;; nset++) { if ((op = nset->cmd) == '-') { clrbits |= nset->bits; setbits &= ~nset->bits; Xbits &= ~nset->bits; } else if (op == '+') { setbits |= nset->bits; clrbits &= ~nset->bits; Xbits &= ~nset->bits; } else if (op == 'X') Xbits |= nset->bits & ~setbits; else break; } if (clrbits) { set->cmd = '-'; set->cmd2 = 0; set->bits = clrbits; set++; } if (setbits) { set->cmd = '+'; set->cmd2 = 0; set->bits = setbits; set++; } if (Xbits) { set->cmd = 'X'; set->cmd2 = 0; set->bits = Xbits; set++; } } } makefs/src/lib/libc/gen/unvis.c010064400000000000000000000141411031461326700136060ustar00/* $OpenBSD: unvis.c,v 1.12 2005/08/08 08:05:34 espie Exp $ */ /*- * 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. */ #include #include #include __RCSID("$MirOS: src/lib/libc/gen/unvis.c,v 1.3 2005/09/22 20:40:01 tg Exp $"); /* * decode driven by state machine */ #define S_GROUND 0 /* haven't seen escape char */ #define S_START 1 /* start decoding special sequence */ #define S_META 2 /* metachar started (M) */ #define S_META1 3 /* metachar more, regular char (-) */ #define S_CTRL 4 /* control char started (^) */ #define S_OCTAL2 5 /* octal digit 2 */ #define S_OCTAL3 6 /* octal digit 3 */ #define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7') /* * unvis - decode characters previously encoded by vis */ int unvis(char *cp, char c, int *astate, int flag) { if (flag & UNVIS_END) { if (*astate == S_OCTAL2 || *astate == S_OCTAL3) { *astate = S_GROUND; return (UNVIS_VALID); } return (*astate == S_GROUND ? UNVIS_NOCHAR : UNVIS_SYNBAD); } switch (*astate) { case S_GROUND: *cp = 0; if (c == '\\') { *astate = S_START; return (0); } *cp = c; return (UNVIS_VALID); case S_START: switch(c) { case '\\': *cp = c; *astate = S_GROUND; return (UNVIS_VALID); case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': *cp = (c - '0'); *astate = S_OCTAL2; return (0); case 'M': *cp = (char) 0200; *astate = S_META; return (0); case '^': *astate = S_CTRL; return (0); case 'n': *cp = '\n'; *astate = S_GROUND; return (UNVIS_VALID); case 'r': *cp = '\r'; *astate = S_GROUND; return (UNVIS_VALID); case 'b': *cp = '\b'; *astate = S_GROUND; return (UNVIS_VALID); case 'a': *cp = '\007'; *astate = S_GROUND; return (UNVIS_VALID); case 'v': *cp = '\v'; *astate = S_GROUND; return (UNVIS_VALID); case 't': *cp = '\t'; *astate = S_GROUND; return (UNVIS_VALID); case 'f': *cp = '\f'; *astate = S_GROUND; return (UNVIS_VALID); case 's': *cp = ' '; *astate = S_GROUND; return (UNVIS_VALID); case 'E': *cp = '\033'; *astate = S_GROUND; return (UNVIS_VALID); case '\n': /* * hidden newline */ *astate = S_GROUND; return (UNVIS_NOCHAR); case '$': /* * hidden marker */ *astate = S_GROUND; return (UNVIS_NOCHAR); } *astate = S_GROUND; return (UNVIS_SYNBAD); case S_META: if (c == '-') *astate = S_META1; else if (c == '^') *astate = S_CTRL; else { *astate = S_GROUND; return (UNVIS_SYNBAD); } return (0); case S_META1: *astate = S_GROUND; *cp |= c; return (UNVIS_VALID); case S_CTRL: if (c == '?') *cp |= 0177; else *cp |= c & 037; *astate = S_GROUND; return (UNVIS_VALID); case S_OCTAL2: /* second possible octal digit */ if (isoctal(c)) { /* * yes - and maybe a third */ *cp = (*cp << 3) + (c - '0'); *astate = S_OCTAL3; return (0); } /* * no - done with current sequence, push back passed char */ *astate = S_GROUND; return (UNVIS_VALIDPUSH); case S_OCTAL3: /* third possible octal digit */ *astate = S_GROUND; if (isoctal(c)) { *cp = (*cp << 3) + (c - '0'); return (UNVIS_VALID); } /* * we were done, push back passed char */ return (UNVIS_VALIDPUSH); default: /* * decoder in unknown state - (probably uninitialized) */ *astate = S_GROUND; return (UNVIS_SYNBAD); } } /* * strunvis - decode src into dst * * Number of chars decoded into dst is returned, -1 on error. * Dst is null terminated. */ int strunvis(char *dst, const char *src) { char c; char *start = dst; int state = 0; while ((c = *src++)) { again: switch (unvis(dst, c, &state, 0)) { case UNVIS_VALID: dst++; break; case UNVIS_VALIDPUSH: dst++; goto again; case 0: case UNVIS_NOCHAR: break; default: *dst = '\0'; return (-1); } } if (unvis(dst, c, &state, UNVIS_END) == UNVIS_VALID) dst++; *dst = '\0'; return (dst - start); } ssize_t strnunvis(char *dst, const char *src, size_t sz) { char c, p; char *start = dst, *end = dst + sz - 1; int state = 0; if (sz > 0) *end = '\0'; while ((c = *src++)) { again: switch (unvis(&p, c, &state, 0)) { case UNVIS_VALID: if (dst < end) *dst = p; dst++; break; case UNVIS_VALIDPUSH: if (dst < end) *dst = p; dst++; goto again; case 0: case UNVIS_NOCHAR: break; default: if (dst <= end) *dst = '\0'; return (-1); } } if (unvis(&p, c, &state, UNVIS_END) == UNVIS_VALID) { if (dst < end) *dst = p; dst++; } if (dst <= end) *dst = '\0'; return (dst - start); } makefs/src/lib/libutil/fparseln.c010064400000000000000000000115701341415476700142520ustar00/* $OpenBSD: fparseln.c,v 1.5 2004/05/28 07:03:47 deraadt Exp $ */ /* $NetBSD: fparseln.c,v 1.7 1999/07/02 15:49:12 simonb Exp $ */ /* * Copyright (c) 2009, 2010 Thorsten Glaser * Copyright (c) 1997 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. */ #include #include #include #include #ifdef GNUPORT #define NEED_FPARSELN_DECL #include "mbsdtree.h" extern char *fgetln(FILE *, size_t *); #else #include "util.h" #endif __RCSID("$MirOS: src/lib/libutil/fparseln.c,v 1.6 2019/01/05 16:22:52 tg Exp $"); static int isescaped(const char *, const char *, int); /* isescaped(): * Return true if the character in *p that belongs to a string * that starts in *sp, is escaped by the escape character esc. */ static int isescaped(const char *sp, const char *p, int esc) { const char *cp; size_t ne; /* No escape character */ if (esc == '\0') return 1; /* Count the number of escape characters that precede ours */ for (ne = 0, cp = p; --cp >= sp && *cp == esc; ne++) continue; /* Return true if odd number of escape characters */ return (ne & 1) != 0; } /* fparseln(): * Read a line from a file parsing continuations ending in \ * and eliminating trailing newlines, or comments starting with * the comment char. */ char * fparseln(FILE *fp, size_t *size, size_t *lineno, const char str[3], int flags) { static const char dstr[3] = { '\\', '\\', '#' }; char *buf = NULL, *ptr, *cp, esc, con, nl, com; size_t s, len = 0; int cnt = 1; if (str == NULL) str = dstr; esc = str[0]; con = str[1]; com = str[2]; /* * XXX: it would be cool to be able to specify the newline character, * but unfortunately, fgetln does not let us */ nl = '\n'; while (cnt) { cnt = 0; if (lineno) (*lineno)++; if ((ptr = fgetln(fp, &s)) == NULL) break; if (s && com) { /* Check and eliminate comments */ for (cp = ptr; cp < ptr + s; cp++) if (*cp == com && !isescaped(ptr, cp, esc)) { s = cp - ptr; cnt = s == 0 && buf == NULL; break; } } if (s && nl) { /* Check and eliminate newlines */ cp = &ptr[s - 1]; if (*cp == nl) s--; /* forget newline */ } if (s && con) { /* Check and eliminate continuations */ cp = &ptr[s - 1]; if (*cp == con && !isescaped(ptr, cp, esc)) { s--; /* forget escape */ cnt = 1; } } if (s == 0 && buf != NULL) continue; if ((cp = realloc(buf, len + s + 1)) == NULL) { free(buf); return NULL; } buf = cp; (void) memcpy(buf + len, ptr, s); len += s; buf[len] = '\0'; } if ((flags & FPARSELN_UNESCALL) != 0 && esc && buf != NULL && strchr(buf, esc) != NULL) { ptr = cp = buf; while (cp[0] != '\0') { int skipesc; while (cp[0] != '\0' && cp[0] != esc) *ptr++ = *cp++; if (cp[0] == '\0' || cp[1] == '\0') break; skipesc = 0; if (cp[1] == com) skipesc += (flags & FPARSELN_UNESCCOMM); if (cp[1] == con) skipesc += (flags & FPARSELN_UNESCCONT); if (cp[1] == esc) skipesc += (flags & FPARSELN_UNESCESC); if (cp[1] != com && cp[1] != con && cp[1] != esc) skipesc = (flags & FPARSELN_UNESCREST); if (skipesc) cp++; else *ptr++ = *cp++; *ptr++ = *cp++; } *ptr = '\0'; len = strlen(buf); } if (size) *size = len; return buf; } #ifdef TEST int main(int, char **); int main(argc, argv) int argc; char **argv; { char *ptr; size_t size, line; line = 0; while ((ptr = fparseln(stdin, &size, &line, NULL, FPARSELN_UNESCALL)) != NULL) printf("line %d (%d) |%s|\n", line, size, ptr); return 0; } /* # This is a test line 1 line 2 \ line 3 # Comment line 4 \# Not comment \\\\ # And a comment \ line 5 \\\ line 6 */ #endif /* TEST */ makefs/src/sbin/mknod/Makefile010064400000000000000000000002401020120074400135270ustar00# $OpenBSD: Makefile,v 1.5 2001/08/21 17:55:47 millert Exp $ PROG= mknod MAN= mknod.8 mkfifo.1 LINKS= ${BINDIR}/mknod ${BINDIR}/mkfifo .include makefs/src/sbin/mknod/mkfifo.1010064400000000000000000000155171341050350300134450ustar00.\" $MirOS: src/sbin/mknod/mkfifo.1,v 1.4 2018/12/25 19:38:16 tg Exp $ .\" $OpenBSD: mkfifo.1,v 1.6 2003/06/02 20:06:15 millert Exp $ .\" $NetBSD: mkfifo.1,v 1.4 1994/12/23 07:16:54 jtc Exp $ .\" .\" Copyright (c) 1990, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" This code is derived from software contributed to Berkeley by .\" the Institute of Electrical and Electronics Engineers, 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. .\" .\" @(#)mkfifo.1 8.2 (Berkeley) 1/5/94 .\" .\"- .\" Copyright (c) 2008, 2009, 2010, 2016, 2018 .\" mirabilos .\"- .\" Try to make GNU groff and AT&T nroff more compatible .\" * ` generates ‘ in gnroff, so use \` .\" * ' generates ’ in gnroff, \' generates ´, so use \*(aq .\" * - generates ‐ in gnroff, \- generates −, so .tr it to - .\" thus use - for hyphens and \- for minus signs and option dashes .\" * ~ is size-reduced and placed atop in groff, so use \*(TI .\" * ^ is size-reduced and placed atop in groff, so use \*(ha .\" * \(en does not work in nroff, so use \*(en .\" * <>| are problematic, so redefine and use \*(Lt\*(Gt\*(Ba .\" Also make sure to use \& *before* a punctuation char that is to not .\" be interpreted as punctuation, and especially with two-letter words .\" but also (after) a period that does not end a sentence (“e.g.\&”). .\" The section after the "doc" macropackage has been loaded contains .\" additional code to convene between the UCB mdoc macropackage (and .\" its variant as BSD mdoc in groff) and the GNU mdoc macropackage. .\" .ie \n(.g \{\ . if \*[.T]ascii .tr \-\N'45' . if \*[.T]latin1 .tr \-\N'45' . if \*[.T]utf8 .tr \-\N'45' . ds <= \[<=] . ds >= \[>=] . ds Rq \[rq] . ds Lq \[lq] . ds sL \(aq . ds sR \(aq . if \*[.T]utf8 .ds sL ` . if \*[.T]ps .ds sL ` . if \*[.T]utf8 .ds sR ' . if \*[.T]ps .ds sR ' . ds aq \(aq . ds TI \(ti . ds ha \(ha . ds en \(en .\} .el \{\ . ds aq ' . ds TI ~ . ds ha ^ . ds en \(em .\} .\" .\" Implement .Dd with the Mdocdate RCS keyword .\" .rn Dd xD .de Dd .ie \\$1$Mdocdate: \{\ . xD \\$2 \\$3, \\$4 .\} .el .xD \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 .. .\" .\" .Dd must come before definition of .Mx, because when called .\" with -mandoc, it might implement .Mx itself, but we want to .\" use our own definition. And .Dd must come *first*, always. .\" .Dd $Mdocdate: December 25 2018 $ .\" .\" Check which macro package we use, and do other -mdoc setup. .\" .ie \n(.g \{\ . if \*[.T]utf8 .tr \[la]\*(Lt . if \*[.T]utf8 .tr \[ra]\*(Gt . ie d volume-ds-1 .ds tT gnu . el .ie d doc-volume-ds-1 .ds tT gnp . el .ds tT bsd .\} .el .ds tT ucb .\" .\" Implement .Mx (MirBSD) .\" .ie "\*(tT"gnu" \{\ . eo . de Mx . nr curr-font \n[.f] . nr curr-size \n[.ps] . ds str-Mx \f[\n[curr-font]]\s[\n[curr-size]u] . ds str-Mx1 \*[Tn-font-size]\%MirBSD\*[str-Mx] . if !\n[arg-limit] \ . if \n[.$] \{\ . ds macro-name Mx . parse-args \$@ . \} . if (\n[arg-limit] > \n[arg-ptr]) \{\ . nr arg-ptr +1 . ie (\n[type\n[arg-ptr]] == 2) \ . as str-Mx1 \~\*[arg\n[arg-ptr]] . el \ . nr arg-ptr -1 . \} . ds arg\n[arg-ptr] "\*[str-Mx1] . nr type\n[arg-ptr] 2 . ds space\n[arg-ptr] "\*[space] . nr num-args (\n[arg-limit] - \n[arg-ptr]) . nr arg-limit \n[arg-ptr] . if \n[num-args] \ . parse-space-vector . print-recursive .. . ec . ds sP \s0 . ds tN \*[Tn-font-size] .\} .el .ie "\*(tT"gnp" \{\ . eo . de Mx . nr doc-curr-font \n[.f] . nr doc-curr-size \n[.ps] . ds doc-str-Mx \f[\n[doc-curr-font]]\s[\n[doc-curr-size]u] . ds doc-str-Mx1 \*[doc-Tn-font-size]\%MirBSD\*[doc-str-Mx] . if !\n[doc-arg-limit] \ . if \n[.$] \{\ . ds doc-macro-name Mx . doc-parse-args \$@ . \} . if (\n[doc-arg-limit] > \n[doc-arg-ptr]) \{\ . nr doc-arg-ptr +1 . ie (\n[doc-type\n[doc-arg-ptr]] == 2) \ . as doc-str-Mx1 \~\*[doc-arg\n[doc-arg-ptr]] . el \ . nr doc-arg-ptr -1 . \} . ds doc-arg\n[doc-arg-ptr] "\*[doc-str-Mx1] . nr doc-type\n[doc-arg-ptr] 2 . ds doc-space\n[doc-arg-ptr] "\*[doc-space] . nr doc-num-args (\n[doc-arg-limit] - \n[doc-arg-ptr]) . nr doc-arg-limit \n[doc-arg-ptr] . if \n[doc-num-args] \ . doc-parse-space-vector . doc-print-recursive .. . ec . ds sP \s0 . ds tN \*[doc-Tn-font-size] .\} .el \{\ . de Mx . nr cF \\n(.f . nr cZ \\n(.s . ds aa \&\f\\n(cF\s\\n(cZ . if \\n(aC==0 \{\ . ie \\n(.$==0 \&MirBSD\\*(aa . el .aV \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 . \} . if \\n(aC>\\n(aP \{\ . nr aP \\n(aP+1 . ie \\n(C\\n(aP==2 \{\ . as b1 \&MirBSD\ #\&\\*(A\\n(aP\\*(aa . ie \\n(aC>\\n(aP \{\ . nr aP \\n(aP+1 . nR . \} . el .aZ . \} . el \{\ . as b1 \&MirBSD\\*(aa . nR . \} . \} .. .\} .\"- .Dt MKFIFO 1 .Os .Sh NAME .Nm mkfifo .Nd make FIFOs .Sh SYNOPSIS .Nm mkfifo .Op Fl m Ar mode .Ar fifo_name ... .Sh DESCRIPTION .Nm mkfifo creates the FIFOs requested, in the order specified, using mode .Li \&0666 modified by the current .Xr umask 2 . .Pp The options are as follows: .Bl -tag -width Ds .It Fl m Ar mode Set the file permission bits of newly created directories to .Ar mode . The mode is specified as in .Xr chmod 1 . In symbolic mode strings, the .Dq + and .Dq \- operators are interpreted relative to an assumed initial mode of .Dq a=rw . .El .Pp .Nm mkfifo requires write permission in the parent directory. .Pp The .Nm mkfifo utility exits 0 on success or \*(Gt0 if an error occurred. .Sh SEE ALSO .Xr mkdir 1 , .Xr rm 1 , .Xr mkfifo 2 , .Xr mknod 8 .Sh STANDARDS The .Nm mkfifo utility is expected to be .St -p1003.2-92 compliant. .Sh HISTORY The .Nm command appeared in .Bx 4.4 . makefs/src/sbin/mknod/mknod.8010064400000000000000000000074571130230070700133140ustar00.\" $OpenBSD: mknod.8,v 1.10 2003/06/02 20:06:15 millert Exp $ .\" $NetBSD: mknod.8,v 1.9 1995/08/10 23:47:32 jtc Exp $ .\" .\" 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. .\" .\" @(#)mknod.8 8.2 (Berkeley) 12/11/93 .\" .Dd $Mdocdate: November 22 2009 $ .Dt MKNOD 8 .Os .Sh NAME .Nm mknod .Nd build special file .Sh SYNOPSIS .Nm mknod .Op Fl m Ar mode .Ar name .Op Cm b | Cm c .Ar major minor .Nm mknod .Op Fl m Ar mode .Ar name .Cm p .Sh DESCRIPTION The .Nm command creates device special files. Normally the shell script .Pa /dev/MAKEDEV is used to create special files for commonly known devices; it executes .Nm with the appropriate arguments and can make all the files required for the device. .Pp The options are as follows: .Bl -tag -width Ds .It Fl m Ar mode Set the file mode. .Ar mode may be absolute or symbolic, as described in .Xr chmod 1 . In symbolic mode strings, the .Ql + and .Ql \- operators are interpreted relative to an assumed initial mode of .Dq a=rw . .El .Pp To make nodes manually, the arguments are: .Bl -tag -width majorx .It Ar name Device or FIFO name. For example .Dq sd for a SCSI disk or a .Dq pty for pseudo-devices. FIFOs may be named arbitrarily by the user. .It Cm b | Cm c | Cm p Type of device or FIFO. If the device is a block type device such as a tape or disk drive which needs both cooked and raw special files, the type is .Cm b . All other devices are character type devices, such as terminal and pseudo devices, and are type .Cm c . A FIFO (also known as a named pipe) is type .Cm p . .It Ar major The major device number is an integer number which tells the kernel which device driver entry point to use. To learn what major device number to use for a particular device, check the file .Pa /dev/MAKEDEV to see if the device is known. .It Ar minor The minor device number tells the kernel which subunit the node corresponds to on the device; for example, a subunit may be a filesystem partition or a tty line. .Pp Major and minor device numbers can be given in any format acceptable to .Xr strtoul 3 , so that a leading .Dq 0x indicates a hexadecimal number, and a leading .Dq 0 will cause the number to be interpreted as octal. .El .Sh SEE ALSO .Xr chmod 1 , .Xr mkfifo 1 , .Xr mkfifo 2 , .Xr mknod 2 , .Xr MAKEDEV 8 .Sh HISTORY A .Nm command appeared in .At v6 . makefs/src/sbin/mknod/mknod.c010064400000000000000000000116051341414421600133640ustar00/* $OpenBSD: mknod.c,v 1.13 2003/06/02 20:06:15 millert Exp $ */ /* $NetBSD: mknod.c,v 1.8 1995/08/11 00:08:18 jtc Exp $ */ /* * Copyright (c) 1989, 1990, 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. */ #include __COPYRIGHT("@(#) Copyright (c) 1989, 1993\n\ The Regents of the University of California. All rights reserved.\n"); __SCCSID("@(#)mknod.c 8.1 (Berkeley) 6/5/93"); __RCSID("$MirOS: src/sbin/mknod/mknod.c,v 1.4 2019/01/05 15:08:35 tg Exp $"); #include #include #ifdef __GLIBC__ #include #endif #include #include #include #include #include #include extern char *__progname; int domknod(int, char **, mode_t); int domkfifo(int, char **, mode_t); void usage(int); int main(int argc, char *argv[]) { int ch, ismkfifo = 0; void *set = NULL; mode_t mode = 0; setlocale (LC_ALL, ""); if (strcmp(__progname, "mkfifo") == 0) ismkfifo = 1; while ((ch = getopt(argc, argv, "m:")) != -1) switch(ch) { case 'm': if (!(set = setmode(optarg))) { errx(1, "invalid file mode."); /* NOTREACHED */ } /* * In symbolic mode strings, the + and - operators are * interpreted relative to an assumed initial mode of * a=rw. */ mode = getmode(set, DEFFILEMODE); free(set); break; case '?': default: usage(ismkfifo); } argc -= optind; argv += optind; if (argv[0] == NULL) usage(ismkfifo); if (!ismkfifo) { if (argc == 2 && argv[1][0] == 'p') { ismkfifo = 2; argc--; argv[1] = NULL; } else if (argc != 4) { usage(ismkfifo); /* NOTREACHED */ } } /* * If the user specified a mode via `-m', don't allow the umask * to modified it. If no `-m' flag was specified, the default * mode is the value of the bitwise inclusive or of S_IRUSR, * S_IWUSR, S_IRGRP, S_IWGRP, S_IROTH, and S_IWOTH as modified by * the umask. */ if (set) (void)umask(0); else mode = DEFFILEMODE; if (ismkfifo) exit(domkfifo(argc, argv, mode)); else exit(domknod(argc, argv, mode)); } int domknod(int argc, char **argv, mode_t mode) { dev_t dev; char *endp; u_int major, minor; if (argv[1][0] == 'c') mode |= S_IFCHR; else if (argv[1][0] == 'b') mode |= S_IFBLK; else { errx(1, "node must be type 'b' or 'c'."); /* NOTREACHED */ } major = (long)strtoul(argv[2], &endp, 0); if (endp == argv[2] || *endp != '\0') { errx(1, "non-numeric major number."); /* NOTREACHED */ } minor = (long)strtoul(argv[3], &endp, 0); if (endp == argv[3] || *endp != '\0') { errx(1, "non-numeric minor number."); /* NOTREACHED */ } dev = makedev(major, minor); if (major(dev) != major || minor(dev) != minor) { errx(1, "major or minor number too large"); /* NOTREACHED */ } if (mknod(argv[0], mode, dev) < 0) { err(1, "%s", argv[0]); /* NOTREACHED */ } return(0); } int domkfifo(int argc, char **argv, mode_t mode) { int rv; for (rv = 0; *argv; ++argv) { if (mkfifo(*argv, mode) < 0) { warn("%s", *argv); rv = 1; } } return(rv); } void usage(int ismkfifo) { if (ismkfifo == 1) (void)fprintf(stderr, "usage: %s [-m mode] fifoname ...\n", __progname); else { (void)fprintf(stderr, "usage: %s [-m mode] name [b | c] major minor\n", __progname); (void)fprintf(stderr, "usage: %s [-m mode] name p\n", __progname); } exit(1); } makefs/src/sys/isofs/cd9660/TODO010064400000000000000000000024541020120115000133710ustar00# $OpenBSD: TODO,v 1.2 1997/02/24 14:30:49 niklas Exp $ # $NetBSD: TODO,v 1.4 1994/07/19 11:34:48 mycroft Exp $ 1) should understand "older", original High Sierra ("CDROM001") type Not yet. ( I don't have this technical information, yet. ) 2) should understand Rock Ridge Yes, we have follows function. o Symbolic Link o Real Name(long name) o File Attribute o Time stamp o uid, gid o Devices o Relocated directories Except follows: o POSIX device number mapping There is some preliminary stuff in there that (ab-)uses the mknod system call, but this needs a writable filesystem 5) should have name translation enabled by mount flag Yes. we can disable the Rock Ridge Extension by follows option; "mount -t isofs -o -norrip /dev/cd0d /cdrom" 6) should run as a user process, and not take up kernel space (cdroms are slow) Not yet. 7) ECMA support. Not yet. we need not only a technical spec but also ECMA format cd-rom itself! 8) Character set change by SVD ( multi SVD support ) Not yet. We should also hack the other part of system as 8 bit clean. As far as I know, if you export the cdrom by NFS, the client can access the 8 bit clean (ie. Solaris Japanese with EUC code ) makefs/src/sys/isofs/cd9660/TODO.hibler010064400000000000000000000016341020120115000146340ustar00# $OpenBSD: TODO.hibler,v 1.4 2003/01/05 22:41:36 deraadt Exp $ # $NetBSD: TODO.hibler,v 1.6 1994/12/13 22:33:10 mycroft Exp $ 1. Investiate making ISOFS another UFS shared filesystem (ala FFS/MFS/LFS). Since it was modelled after the inode code, we might be able to merge them back. It looks like a separate (but very similar) lookup routine will be needed due to the associated file stuff. 2. It would be nice to be able to use the vfs_cluster code. Unfortunately, if the logical block size is smaller than the page size, it won't work. Also, if throughput is relatively constant for any block size (as it is for the HP drive--150kbs) then clustering may not buy much (or may even hurt when vfs_cluster comes up with a large sync cluster). 3. Seems like there should be a "notrans" or some such mount option to show filenames as they really are without lower-casing. Does this make sense? makefs/src/sys/isofs/cd9660/cd9660_bmap.c010064400000000000000000000065251020120115000147620ustar00/* $OpenBSD: cd9660_bmap.c,v 1.4 2003/06/02 23:28:05 millert Exp $ */ /* $NetBSD: cd9660_bmap.c,v 1.7 1997/01/24 00:27:29 cgd Exp $ */ /*- * Copyright (c) 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. * * @(#)cd9660_bmap.c 8.4 (Berkeley) 12/5/94 */ #include #include #include #include #include #include #include #include #include /* * Bmap converts a the logical block number of a file to its physical block * number on the disk. The conversion is done by using the logical block * number to index into the data block (extent) for the file. */ int cd9660_bmap(v) void *v; { struct vop_bmap_args /* { struct vnode *a_vp; daddr_t a_bn; struct vnode **a_vpp; daddr_t *a_bnp; int *a_runp; } */ *ap = v; struct iso_node *ip = VTOI(ap->a_vp); daddr_t lblkno = ap->a_bn; int bshift; /* * Check for underlying vnode requests and ensure that logical * to physical mapping is requested. */ if (ap->a_vpp != NULL) *ap->a_vpp = ip->i_devvp; if (ap->a_bnp == NULL) return (0); /* * Compute the requested block number */ bshift = ip->i_mnt->im_bshift; *ap->a_bnp = (ip->iso_start + lblkno) << (bshift - DEV_BSHIFT); /* * Determine maximum number of readahead blocks following the * requested block. */ if (ap->a_runp) { int nblk; nblk = (ip->i_size >> bshift) - (lblkno + 1); if (nblk <= 0) *ap->a_runp = 0; else if (nblk >= (MAXBSIZE >> bshift)) *ap->a_runp = (MAXBSIZE >> bshift) - 1; else *ap->a_runp = nblk; } return (0); } makefs/src/sys/isofs/cd9660/cd9660_extern.h010064400000000000000000000102411314214545000153640ustar00/* $OpenBSD: cd9660_extern.h,v 1.8 2003/06/02 23:28:05 millert Exp $ */ /* $NetBSD: cd9660_extern.h,v 1.1 1997/01/24 00:24:53 cgd Exp $ */ /*- * Copyright (c) 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. * * @(#)iso.h 8.4 (Berkeley) 12/5/94 */ /* * Definitions used in the kernel for cd9660 filesystem support. */ /* CD-ROM Format type */ enum ISO_FTYPE { ISO_FTYPE_DEFAULT, ISO_FTYPE_9660, ISO_FTYPE_RRIP, ISO_FTYPE_ECMA }; #ifndef ISOFSMNT_ROOT #define ISOFSMNT_ROOT 0 #endif struct iso_mnt { int im_flags; struct mount *im_mountp; dev_t im_dev; struct vnode *im_devvp; int logical_block_size; int im_bshift; int im_bmask; int volume_space_size; struct netexport im_export; char root[ISODCL (157, 190)]; int root_extent; int root_size; enum ISO_FTYPE iso_ftype; int rr_skip; int rr_skip0; int joliet_level; }; #define VFSTOISOFS(mp) ((struct iso_mnt *)((mp)->mnt_data)) #define blkoff(imp, loc) ((loc) & (imp)->im_bmask) #define lblktosize(imp, blk) ((blk) << (imp)->im_bshift) #define lblkno(imp, loc) ((loc) >> (imp)->im_bshift) #define blksize(imp, ip, lbn) ((imp)->logical_block_size) int cd9660_mount(struct mount *, const char *, void *, struct nameidata *, struct proc *); int cd9660_start(struct mount *, int, struct proc *); int cd9660_unmount(struct mount *, int, struct proc *); int cd9660_root(struct mount *, struct vnode **); int cd9660_quotactl(struct mount *, int, uid_t, caddr_t, struct proc *); int cd9660_statfs(struct mount *, struct statfs *, struct proc *); int cd9660_sync(struct mount *, int, struct ucred *, struct proc *); int cd9660_vget(struct mount *, ino_t, struct vnode **); int cd9660_fhtovp(struct mount *, struct fid *, struct vnode **); int cd9660_vptofh(struct vnode *, struct fid *); int cd9660_init(struct vfsconf *); int cd9660_check_export(struct mount *, struct mbuf *, int *, struct ucred **); #define cd9660_sysctl ((int (*)(int *, u_int, void *, size_t *, void *, \ size_t, struct proc *))eopnotsupp) int cd9660_mountroot(void); extern int (**cd9660_vnodeop_p)(void *); extern int (**cd9660_specop_p)(void *); #ifdef FIFO extern int (**cd9660_fifoop_p)(void *); #endif int isochar(const u_char *, const u_char *, int, u_char *); int isofncmp(const u_char *, int, const u_char *, int, int); void isofntrans(u_char *, int, u_char *, u_short *, int, int, int); ino_t isodirino(struct iso_directory_record *, struct iso_mnt *); makefs/src/sys/isofs/cd9660/cd9660_lookup.c010064400000000000000000000334061314214545000153730ustar00/* $OpenBSD: cd9660_lookup.c,v 1.11 2003/06/02 23:28:05 millert Exp $ */ /* $NetBSD: cd9660_lookup.c,v 1.18 1997/05/08 16:19:59 mycroft Exp $ */ /*- * Copyright (c) 1989, 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. * * from: @(#)ufs_lookup.c 7.33 (Berkeley) 5/19/91 * * @(#)cd9660_lookup.c 8.5 (Berkeley) 12/5/94 */ #include #include #include #include #include #include #include #include #include #include #include #include struct nchstats iso_nchstats; /* * Convert a component of a pathname into a pointer to a locked inode. * This is a very central and rather complicated routine. * If the filesystem is not maintained in a strict tree hierarchy, * this can result in a deadlock situation (see comments in code below). * * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on * whether the name is to be looked up, created, renamed, or deleted. * When CREATE, RENAME, or DELETE is specified, information usable in * creating, renaming, or deleting a directory entry may be calculated. * If flag has LOCKPARENT or'ed into it and the target of the pathname * exists, lookup returns both the target and its parent directory locked. * When creating or renaming and LOCKPARENT is specified, the target may * not be ".". When deleting and LOCKPARENT is specified, the target may * be "."., but the caller must check to ensure it does an vrele and iput * instead of two iputs. * * Overall outline of cd9660_lookup: * * check accessibility of directory * look for name in cache, if found, then if at end of path * and deleting or creating, drop it, else return name * search for name in directory, to found or notfound * notfound: * if creating, return locked directory, leaving info on available slots * else return error * found: * if at end of path and deleting, return information to allow delete * if at end of path and rewriting (RENAME and LOCKPARENT), lock target * inode and return info to allow rewrite * if not at end, add name to cache; if at end and neither creating * nor deleting, add name to cache * * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent inode unlocked. */ int cd9660_lookup(v) void *v; { struct vop_lookup_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; } */ *ap = v; register struct vnode *vdp; /* vnode for directory being searched */ register struct iso_node *dp; /* inode for directory being searched */ register struct iso_mnt *imp; /* filesystem that directory is in */ struct buf *bp; /* a buffer of directory entries */ struct iso_directory_record *ep = NULL; /* the current directory entry */ int entryoffsetinblock; /* offset of ep in bp's buffer */ int saveoffset = -1; /* offset of last directory entry in dir */ int numdirpasses; /* strategy for directory search */ doff_t endsearch; /* offset to end directory search */ struct vnode *pdp; /* saved dp during symlink work */ struct vnode *tdp; /* returned by cd9660_vget_internal */ u_long bmask; /* block offset mask */ int lockparent; /* 1 => lockparent flag is set */ int wantparent; /* 1 => wantparent or lockparent flag */ int error; ino_t ino = 0; int reclen; u_short namelen; char altname[NAME_MAX]; int res; int assoc, len; char *name; struct vnode **vpp = ap->a_vpp; struct componentname *cnp = ap->a_cnp; struct ucred *cred = cnp->cn_cred; int flags; int nameiop = cnp->cn_nameiop; struct proc *p = cnp->cn_proc; cnp->cn_flags &= ~PDIRUNLOCK; flags = cnp->cn_flags; bp = NULL; *vpp = NULL; vdp = ap->a_dvp; dp = VTOI(vdp); imp = dp->i_mnt; lockparent = flags & LOCKPARENT; wantparent = flags & (LOCKPARENT|WANTPARENT); /* * Check accessiblity of directory. */ if ((error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_proc)) != 0) return (error); if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) && (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) return (EROFS); /* * We now have a segment name to search for, and a directory to search. * * Before tediously performing a linear scan of the directory, * check the name cache to see if the directory/name pair * we are looking for is known already. */ if ((error = cache_lookup(vdp, vpp, cnp)) >= 0) return (error); len = cnp->cn_namelen; name = cnp->cn_nameptr; /* * A leading `=' means, we are looking for an associated file */ assoc = (imp->iso_ftype != ISO_FTYPE_RRIP && *name == ASSOCCHAR); if (assoc) { len--; name++; } /* * If there is cached information on a previous search of * this directory, pick up where we last left off. * We cache only lookups as these are the most common * and have the greatest payoff. Caching CREATE has little * benefit as it usually must search the entire directory * to determine that the entry does not exist. Caching the * location of the last DELETE or RENAME has not reduced * profiling time and hence has been removed in the interest * of simplicity. */ bmask = imp->im_bmask; if (nameiop != LOOKUP || dp->i_diroff == 0 || dp->i_diroff > dp->i_size) { entryoffsetinblock = 0; dp->i_offset = 0; numdirpasses = 1; } else { dp->i_offset = dp->i_diroff; if ((entryoffsetinblock = dp->i_offset & bmask) && (error = cd9660_bufatoff(dp, (off_t)dp->i_offset, NULL, &bp))) return (error); numdirpasses = 2; iso_nchstats.ncs_2passes++; } endsearch = dp->i_size; searchloop: while (dp->i_offset < endsearch) { /* * If offset is on a block boundary, * read the next directory block. * Release previous if it exists. */ if ((dp->i_offset & bmask) == 0) { if (bp != NULL) brelse(bp); error = cd9660_bufatoff(dp, (off_t)dp->i_offset, NULL, &bp); if (error) return (error); entryoffsetinblock = 0; } /* * Get pointer to next entry. */ ep = (struct iso_directory_record *) ((char *)bp->b_data + entryoffsetinblock); reclen = isonum_711(ep->length); if (reclen == 0) { /* skip to next block, if any */ dp->i_offset = (dp->i_offset & ~bmask) + imp->logical_block_size; continue; } if (reclen < ISO_DIRECTORY_RECORD_SIZE) /* illegal entry, stop */ break; if (entryoffsetinblock + reclen > imp->logical_block_size) /* entries are not allowed to cross boundaries */ break; namelen = isonum_711(ep->name_len); if (reclen < ISO_DIRECTORY_RECORD_SIZE + namelen) /* illegal entry, stop */ break; /* * Check for a name match. */ switch (imp->iso_ftype) { default: if ((!(isonum_711(ep->flags)&4)) == !assoc) { if ((len == 1 && *name == '.') || (flags & ISDOTDOT)) { if (namelen == 1 && ep->name[0] == ((flags & ISDOTDOT) ? 1 : 0)) { /* * Save directory entry's inode number and * release directory buffer. */ dp->i_ino = isodirino(ep, imp); goto found; } if (namelen != 1 || ep->name[0] != 0) goto notfound; } else if (!(res = isofncmp(name, len, ep->name, namelen, imp->joliet_level))) { if (isonum_711(ep->flags)&2) ino = isodirino(ep, imp); else ino = dbtob(bp->b_blkno) + entryoffsetinblock; saveoffset = dp->i_offset; } else if (ino) goto foundino; #ifdef NOSORTBUG /* On some CDs directory entries are not sorted correctly */ else if (res < 0) goto notfound; else if (res > 0 && numdirpasses == 2) numdirpasses++; #endif } break; case ISO_FTYPE_RRIP: if (isonum_711(ep->flags)&2) ino = isodirino(ep, imp); else ino = dbtob(bp->b_blkno) + entryoffsetinblock; dp->i_ino = ino; cd9660_rrip_getname(ep,altname,&namelen,&dp->i_ino,imp); if (namelen == cnp->cn_namelen && !bcmp(name,altname,namelen)) goto found; ino = 0; break; } dp->i_offset += reclen; entryoffsetinblock += reclen; } if (ino) { foundino: dp->i_ino = ino; if (saveoffset != dp->i_offset) { if (lblkno(imp, dp->i_offset) != lblkno(imp, saveoffset)) { if (bp != NULL) brelse(bp); if ((error = cd9660_bufatoff(dp, (off_t)saveoffset, NULL, &bp)) != 0) return (error); } entryoffsetinblock = saveoffset & bmask; ep = (struct iso_directory_record *) ((char *)bp->b_data + entryoffsetinblock); dp->i_offset = saveoffset; } goto found; } notfound: /* * If we started in the middle of the directory and failed * to find our target, we must check the beginning as well. */ if (numdirpasses == 2) { numdirpasses--; dp->i_offset = 0; endsearch = dp->i_diroff; goto searchloop; } if (bp != NULL) brelse(bp); /* * Insert name into cache (as non-existent) if appropriate. */ if (cnp->cn_flags & MAKEENTRY) cache_enter(vdp, *vpp, cnp); if (nameiop == CREATE || nameiop == RENAME) return (EJUSTRETURN); return (ENOENT); found: if (numdirpasses == 2) iso_nchstats.ncs_pass2++; /* * Found component in pathname. * If the final component of path name, save information * in the cache as to where the entry was found. */ if ((flags & ISLASTCN) && nameiop == LOOKUP) dp->i_diroff = dp->i_offset; /* * Step through the translation in the name. We do not `iput' the * directory because we may need it again if a symbolic link * is relative to the current directory. Instead we save it * unlocked as "pdp". We must get the target inode before unlocking * the directory to insure that the inode will not be removed * before we get it. We prevent deadlock by always fetching * inodes from the root, moving down the directory tree. Thus * when following backward pointers ".." we must unlock the * parent directory before getting the requested directory. * There is a potential race condition here if both the current * and parent directories are removed before the `iget' for the * inode associated with ".." returns. We hope that this occurs * infrequently since we cannot avoid this race condition without * implementing a sophisticated deadlock detection algorithm. * Note also that this simple deadlock detection scheme will not * work if the filesystem has any hard links other than ".." * that point backwards in the directory structure. */ pdp = vdp; /* * If ino is different from dp->i_ino, * it's a relocated directory. */ if (flags & ISDOTDOT) { brelse(bp); VOP_UNLOCK(pdp, 0, p); /* race to get the inode */ cnp->cn_flags |= PDIRUNLOCK; error = cd9660_vget_internal(vdp->v_mount, dp->i_ino, &tdp, dp->i_ino != ino, NULL); if (error) { if (vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p) == 0) cnp->cn_flags &= ~PDIRUNLOCK; return (error); } if (lockparent && (flags & ISLASTCN)) { if ((error = vn_lock(pdp, LK_EXCLUSIVE, p))) { vput(tdp); return (error); } cnp->cn_flags &= ~PDIRUNLOCK; } *vpp = tdp; } else if (dp->i_number == dp->i_ino) { brelse(bp); VREF(vdp); /* we want ourself, ie "." */ *vpp = vdp; } else { error = cd9660_vget_internal(vdp->v_mount, dp->i_ino, &tdp, dp->i_ino != ino, ep); brelse(bp); if (error) return (error); if (!lockparent || !(flags & ISLASTCN)) { VOP_UNLOCK(pdp, 0, p); cnp->cn_flags |= PDIRUNLOCK; } *vpp = tdp; } /* * Insert name into cache if appropriate. */ if (cnp->cn_flags & MAKEENTRY) cache_enter(vdp, *vpp, cnp); return (0); } /* * Return buffer with the contents of block "offset" from the beginning of * directory "ip". If "res" is non-zero, fill it in with a pointer to the * remaining space in the directory. */ int cd9660_bufatoff(struct iso_node *ip, off_t offset, char **res, struct buf **bpp) { struct iso_mnt *imp; struct buf *bp; daddr_t lbn; int bsize, error; struct vnode *vp = ITOV(ip); imp = ip->i_mnt; lbn = lblkno(imp, offset); bsize = blksize(imp, ip, lbn); if ((error = bread(vp, lbn, bsize, NOCRED, &bp)) != 0) { brelse(bp); *bpp = NULL; return (error); } if (res) *res = (char *)bp->b_data + blkoff(imp, offset); *bpp = bp; return (0); } makefs/src/sys/isofs/cd9660/cd9660_node.c010064400000000000000000000273301166225623000150120ustar00/** $MirOS: src/sys/isofs/cd9660/cd9660_node.c,v 1.3 2011/11/20 20:01:04 tg Exp $ */ /* $OpenBSD: cd9660_node.c,v 1.14 2003/06/02 23:28:05 millert Exp $ */ /* $NetBSD: cd9660_node.c,v 1.17 1997/05/05 07:13:57 mycroft Exp $ */ /*- * Copyright (c) 2008 * Thorsten Glaser * Copyright (c) 1982, 1986, 1989, 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. * * @(#)cd9660_node.c 8.5 (Berkeley) 12/5/94 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Structures associated with iso_node caching. */ struct iso_node **isohashtbl; u_long isohash; #define INOHASH(device, inum) (((device) + ((inum)>>12)) & isohash) struct simplelock cd9660_ihash_slock; #ifdef ISODEVMAP struct iso_node **idvhashtbl; u_long idvhash; #define DNOHASH(device, inum) (((device) + ((inum)>>12)) & idvhash) #endif extern int prtactive; /* 1 => print out reclaim of active vnodes */ static u_int cd9660_chars2ui(u_char *, int); /* * Initialize hash links for inodes and dnodes. */ int cd9660_init(vfsp) struct vfsconf *vfsp; { isohashtbl = hashinit(desiredvnodes, M_ISOFSMNT, M_WAITOK, &isohash); simple_lock_init(&cd9660_ihash_slock); #ifdef ISODEVMAP idvhashtbl = hashinit(desiredvnodes / 8, M_ISOFSMNT, M_WAITOK, &idvhash); #endif return (0); } #ifdef ISODEVMAP /* * Enter a new node into the device hash list */ struct iso_dnode * iso_dmap(device, inum, create) dev_t device; ino_t inum; int create; { register struct iso_dnode **dpp, *dp, *dq; dpp = &idvhashtbl[DNOHASH(device, inum)]; for (dp = *dpp;; dp = dp->d_next) { if (dp == NULL) return (NULL); if (inum == dp->i_number && device == dp->i_dev) return (dp); } if (!create) return (NULL); MALLOC(dp, struct iso_dnode *, sizeof(struct iso_dnode), M_CACHE, M_WAITOK); dp->i_dev = dev; dp->i_number = ino; if (dq = *dpp) dq->d_prev = dp->d_next; dp->d_next = dq; dp->d_prev = dpp; *dpp = dp; return (dp); } void iso_dunmap(device) dev_t device; { struct iso_dnode **dpp, *dp, *dq; for (dpp = idvhashtbl; dpp <= idvhashtbl + idvhash; dpp++) { for (dp = *dpp; dp != NULL; dp = dq) { dq = dp->d_next; if (device == dp->i_dev) { if (dq) dq->d_prev = dp->d_prev; *dp->d_prev = dq; FREE(dp, M_CACHE); } } } } #endif /* * Use the device/inum pair to find the incore inode, and return a pointer * to it. If it is in core, but locked, wait for it. */ struct vnode * cd9660_ihashget(dev, inum) dev_t dev; ino_t inum; { struct proc *p = curproc; /* XXX */ struct iso_node *ip; struct vnode *vp; loop: simple_lock(&cd9660_ihash_slock); for (ip = isohashtbl[INOHASH(dev, inum)]; ip; ip = ip->i_next) { if (inum == ip->i_number && dev == ip->i_dev) { vp = ITOV(ip); simple_lock(&vp->v_interlock); simple_unlock(&cd9660_ihash_slock); if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, p)) goto loop; return (vp); } } simple_unlock(&cd9660_ihash_slock); return (NULL); } /* * Insert the inode into the hash table, and return it locked. */ int cd9660_ihashins(ip) struct iso_node *ip; { struct proc *p = curproc; struct iso_node **ipp, *iq; simple_lock(&cd9660_ihash_slock); ipp = &isohashtbl[INOHASH(ip->i_dev, ip->i_number)]; for (iq = *ipp; iq; iq = iq->i_next) { if (iq->i_dev == ip->i_dev && iq->i_number == ip->i_number) return (EEXIST); } if ((iq = *ipp) != NULL) iq->i_prev = &ip->i_next; ip->i_next = iq; ip->i_prev = ipp; *ipp = ip; simple_unlock(&cd9660_ihash_slock); lockmgr(&ip->i_lock, LK_EXCLUSIVE, 0, p); return (0); } /* * Remove the inode from the hash table. */ void cd9660_ihashrem(ip) register struct iso_node *ip; { register struct iso_node *iq; if (ip->i_prev == NULL) return; simple_lock(&cd9660_ihash_slock); if ((iq = ip->i_next) != NULL) iq->i_prev = ip->i_prev; *ip->i_prev = iq; #ifdef DIAGNOSTIC ip->i_next = NULL; ip->i_prev = NULL; #endif simple_unlock(&cd9660_ihash_slock); } /* * Last reference to an inode, write the inode out and if necessary, * truncate and deallocate the file. */ int cd9660_inactive(v) void *v; { struct vop_inactive_args /* { struct vnode *a_vp; struct proc *a_p; } */ *ap = v; struct vnode *vp = ap->a_vp; struct proc *p = ap->a_p; register struct iso_node *ip = VTOI(vp); int error = 0; if (prtactive && vp->v_usecount != 0) vprint("cd9660_inactive: pushing active", vp); ip->i_flag = 0; VOP_UNLOCK(vp, 0, p); /* * If we are done with the inode, reclaim it * so that it can be reused immediately. */ if (ip->inode.iso_mode == 0) vrecycle(vp, (struct simplelock *)0, p); return (error); } /* * Reclaim an inode so that it can be used for other purposes. */ int cd9660_reclaim(v) void *v; { struct vop_reclaim_args /* { struct vnode *a_vp; } */ *ap = v; register struct vnode *vp = ap->a_vp; register struct iso_node *ip = VTOI(vp); if (prtactive && vp->v_usecount != 0) vprint("cd9660_reclaim: pushing active", vp); /* * Remove the inode from its hash chain. */ cd9660_ihashrem(ip); /* * Purge old data structures associated with the inode. */ cache_purge(vp); if (ip->i_devvp) { vrele(ip->i_devvp); ip->i_devvp = 0; } FREE(vp->v_data, M_ISOFSNODE); vp->v_data = NULL; return (0); } /* * File attributes */ void cd9660_defattr(isodir, inop, bp) struct iso_directory_record *isodir; struct iso_node *inop; struct buf *bp; { struct buf *bp2 = NULL; struct iso_mnt *imp; struct iso_extended_attributes *ap = NULL; int off; if (isonum_711(isodir->flags)&2) { inop->inode.iso_mode = S_IFDIR; /* * If we return 2, fts() will assume there are no subdirectories * (just links for the path and .), so instead we return 1. */ inop->inode.iso_links = 1; } else { inop->inode.iso_mode = S_IFREG; inop->inode.iso_links = 1; } if (!bp && ((imp = inop->i_mnt)->im_flags & ISOFSMNT_EXTATT) && (off = isonum_711(isodir->ext_attr_length))) { cd9660_bufatoff(inop, (off_t)-(off << imp->im_bshift), NULL, &bp2); bp = bp2; } if (bp) { ap = (struct iso_extended_attributes *)bp->b_data; if (isonum_711(ap->version) == 1) { if (!(ap->perm[1]&0x10)) inop->inode.iso_mode |= S_IRUSR; if (!(ap->perm[1]&0x40)) inop->inode.iso_mode |= S_IXUSR; if (!(ap->perm[0]&0x01)) inop->inode.iso_mode |= S_IRGRP; if (!(ap->perm[0]&0x04)) inop->inode.iso_mode |= S_IXGRP; if (!(ap->perm[0]&0x10)) inop->inode.iso_mode |= S_IROTH; if (!(ap->perm[0]&0x40)) inop->inode.iso_mode |= S_IXOTH; inop->inode.iso_uid = isonum_723(ap->owner); /* what about 0? */ inop->inode.iso_gid = isonum_723(ap->group); /* what about 0? */ } else ap = NULL; } if (!ap) { inop->inode.iso_mode |= S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; inop->inode.iso_uid = (uid_t)0; inop->inode.iso_gid = (gid_t)0; } if (bp2) brelse(bp2); } /* * Time stamps */ void cd9660_deftstamp(isodir,inop,bp) struct iso_directory_record *isodir; struct iso_node *inop; struct buf *bp; { struct buf *bp2 = NULL; struct iso_mnt *imp; struct iso_extended_attributes *ap = NULL; int off; if (!bp && ((imp = inop->i_mnt)->im_flags & ISOFSMNT_EXTATT) && (off = isonum_711(isodir->ext_attr_length))) { cd9660_bufatoff(inop, (off_t)-(off << imp->im_bshift), NULL, &bp2); bp = bp2; } if (bp) { ap = (struct iso_extended_attributes *)bp->b_data; if (isonum_711(ap->version) == 1) { if (!cd9660_tstamp_conv17(ap->ftime,&inop->inode.iso_atime)) cd9660_tstamp_conv17(ap->ctime,&inop->inode.iso_atime); if (!cd9660_tstamp_conv17(ap->ctime,&inop->inode.iso_ctime)) inop->inode.iso_ctime = inop->inode.iso_atime; if (!cd9660_tstamp_conv17(ap->mtime,&inop->inode.iso_mtime)) inop->inode.iso_mtime = inop->inode.iso_ctime; } else ap = NULL; } if (!ap) { cd9660_tstamp_conv7(isodir->date,&inop->inode.iso_ctime); inop->inode.iso_atime = inop->inode.iso_ctime; inop->inode.iso_mtime = inop->inode.iso_ctime; } if (bp2) brelse(bp2); } int cd9660_tstamp_conv7(u_char *pi, struct timespec *pu) { struct tm tm; tm.tm_year = pi[0]; tm.tm_mon = pi[1] - 1; tm.tm_mday = pi[2]; tm.tm_hour = pi[3]; tm.tm_min = pi[4]; tm.tm_sec = pi[5]; tm.tm_gmtoff = (signed char)pi[6]; /* timezone offset is unreliable on some disks */ if (-48 <= tm.tm_gmtoff && tm.tm_gmtoff <= 52) tm.tm_gmtoff *= 15 * 60; else tm.tm_gmtoff = 0; pu->tv_sec = tm2timet(&tm); pu->tv_nsec = 0; return (1); } static u_int cd9660_chars2ui(begin,len) u_char *begin; int len; { u_int rc; for (rc = 0; --len >= 0;) { rc *= 10; rc += *begin++ - '0'; } return (rc); } int cd9660_tstamp_conv17(u_char *pi, struct timespec *pu) { struct tm tm; /* year:"0001"-"9999" -> -1900 */ tm.tm_year = cd9660_chars2ui(pi,4) - 1900; /* month: " 1"-"12" -> 0 - 11 */ tm.tm_mon = cd9660_chars2ui(pi + 4,2) - 1; /* day: " 1"-"31" -> 1 - 31 */ tm.tm_mday = cd9660_chars2ui(pi + 6,2); /* hour: " 0"-"23" -> 0 - 23 */ tm.tm_hour = cd9660_chars2ui(pi + 8,2); /* minute:" 0"-"59" -> 0 - 59 */ tm.tm_min = cd9660_chars2ui(pi + 10,2); /* second:" 0"-"59" -> 0 - 60 */ tm.tm_sec = cd9660_chars2ui(pi + 12,2); /* difference of GMT */ tm.tm_gmtoff = (signed char)pi[16]; /* timezone offset is unreliable on some disks */ if (-48 <= tm.tm_gmtoff && tm.tm_gmtoff <= 52) tm.tm_gmtoff *= 15 * 60; else tm.tm_gmtoff = 0; pu->tv_sec = tm2timet(&tm); pu->tv_nsec = 0; return (1); } ino_t isodirino(isodir, imp) struct iso_directory_record *isodir; struct iso_mnt *imp; { ino_t ino; ino = (isonum_733(isodir->extent) + isonum_711(isodir->ext_attr_length)) << imp->im_bshift; return (ino); } makefs/src/sys/isofs/cd9660/cd9660_node.h010064400000000000000000000125711026206251200150110ustar00/* $OpenBSD: cd9660_node.h,v 1.17 2004/10/04 23:37:37 millert Exp $ */ /* $NetBSD: cd9660_node.h,v 1.15 1997/04/11 21:52:01 kleink Exp $ */ /*- * Copyright (c) 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. * * @(#)cd9660_node.h 8.4 (Berkeley) 12/5/94 */ #include #define doff_t u_quad_t typedef struct { struct timespec iso_atime; /* time of last access */ struct timespec iso_mtime; /* time of last modification */ struct timespec iso_ctime; /* time file changed */ u_short iso_mode; /* files access mode and type */ uid_t iso_uid; /* owner user id */ gid_t iso_gid; /* owner group id */ short iso_links; /* links of file */ dev_t iso_rdev; /* Major/Minor number for special */ } ISO_RRIP_INODE; #ifdef ISODEVMAP /* * FOr device# (major,minor) translation table */ struct iso_dnode { struct iso_dnode *d_next, **d_prev; /* hash chain */ dev_t i_dev; /* device where dnode resides */ ino_t i_number; /* the identity of the inode */ dev_t d_dev; /* device # for translation */ }; #endif struct iso_node { struct iso_node *i_next, **i_prev; /* hash chain */ struct vnode *i_vnode; /* vnode associated with this inode */ struct vnode *i_devvp; /* vnode for block I/O */ u_int i_flag; /* see below */ dev_t i_dev; /* device where inode resides */ ino_t i_number; /* the identity of the inode */ /* we use the actual starting block of the file */ struct iso_mnt *i_mnt; /* filesystem associated with this inode */ struct lockf *i_lockf; /* head of byte-level lock list */ doff_t i_endoff; /* end of useful stuff in directory */ doff_t i_diroff; /* offset in dir, where we found last entry */ doff_t i_offset; /* offset of free space in directory */ ino_t i_ino; /* inode number of found directory */ struct lock i_lock; /* node lock */ doff_t iso_extent; /* extent of file */ doff_t i_size; /* * Actual start of data file (may be different from iso_extent, if the * file has extended attributes). */ doff_t iso_start; ISO_RRIP_INODE inode; struct cluster_info i_ci; }; #define i_forw i_chain[0] #define i_back i_chain[1] /* flags */ #define IN_ACCESS 0x0020 /* inode access time to be updated */ #define VTOI(vp) ((struct iso_node *)(vp)->v_data) #define ITOV(ip) ((ip)->i_vnode) /* * Prototypes for ISOFS vnode operations */ int cd9660_lookup(void *); int cd9660_open(void *); int cd9660_close(void *); int cd9660_access(void *); int cd9660_getattr(void *); int cd9660_setattr(void *); int cd9660_read(void *); int cd9660_ioctl(void *); int cd9660_poll(void *); int cd9660_mmap(void *); int cd9660_seek(void *); int cd9660_readdir(void *); int cd9660_readlink(void *); int cd9660_abortop(void *); int cd9660_inactive(void *); int cd9660_reclaim(void *); int cd9660_link(void *); int cd9660_symlink(void *); int cd9660_bmap(void *); int cd9660_lock(void *); int cd9660_unlock(void *); int cd9660_strategy(void *); int cd9660_print(void *); int cd9660_islocked(void *); int cd9660_pathconf(void *); int cd9660_bufatoff(struct iso_node *, off_t, char **, struct buf **); void cd9660_defattr(struct iso_directory_record *, struct iso_node *, struct buf *); void cd9660_deftstamp(struct iso_directory_record *, struct iso_node *, struct buf *); struct vnode *cd9660_ihashget(dev_t, ino_t); int cd9660_ihashins(struct iso_node *); void cd9660_ihashrem(struct iso_node *); int cd9660_tstamp_conv7(u_char *, struct timespec *); int cd9660_tstamp_conv17(u_char *, struct timespec *); int cd9660_vget_internal(struct mount *, ino_t, struct vnode **, int, struct iso_directory_record *); ino_t isodirino(struct iso_directory_record *, struct iso_mnt *); #ifdef ISODEVMAP struct iso_dnode *iso_dmap(dev_t, ino_t, int); void iso_dunmap(dev_t); #endif makefs/src/sys/isofs/cd9660/cd9660_rrip.c010064400000000000000000000440071341415427200150410ustar00/** $MirOS: src/sys/isofs/cd9660/cd9660_rrip.c,v 1.3 2019/01/05 16:17:35 tg Exp $ */ /* $OpenBSD: cd9660_rrip.c,v 1.8 2003/06/02 23:28:05 millert Exp $ */ /* $NetBSD: cd9660_rrip.c,v 1.17 1997/01/24 00:27:32 cgd Exp $ */ /*- * Copyright (c) 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. * * @(#)cd9660_rrip.c 8.6 (Berkeley) 12/5/94 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef GNUPORT extern size_t strlcpy(char *, const char *, size_t); #endif typedef struct { char type[2]; int (*func)(void *, ISO_RRIP_ANALYZE *); void (*func2)(void *, ISO_RRIP_ANALYZE *); int result; } RRIP_TABLE; static int cd9660_rrip_attr(void *, ISO_RRIP_ANALYZE *); static void cd9660_rrip_defattr(void *, ISO_RRIP_ANALYZE *); static int cd9660_rrip_slink(void *, ISO_RRIP_ANALYZE *); static int cd9660_rrip_altname(void *, ISO_RRIP_ANALYZE *); static void cd9660_rrip_defname(void *, ISO_RRIP_ANALYZE *); static int cd9660_rrip_pclink(void *, ISO_RRIP_ANALYZE *); static int cd9660_rrip_reldir(void *, ISO_RRIP_ANALYZE *); static int cd9660_rrip_tstamp(void *, ISO_RRIP_ANALYZE *); static void cd9660_rrip_deftstamp(void *, ISO_RRIP_ANALYZE *); static int cd9660_rrip_device(void *, ISO_RRIP_ANALYZE *); static int cd9660_rrip_idflag(void *, ISO_RRIP_ANALYZE *); static int cd9660_rrip_cont(void *, ISO_RRIP_ANALYZE *); static int cd9660_rrip_stop(void *, ISO_RRIP_ANALYZE *); static int cd9660_rrip_extref(void *, ISO_RRIP_ANALYZE *); static int cd9660_rrip_loop(struct iso_directory_record *, ISO_RRIP_ANALYZE *, RRIP_TABLE *); /* * POSIX file attribute */ static int cd9660_rrip_attr(v, ana) void *v; ISO_RRIP_ANALYZE *ana; { ISO_RRIP_ATTR *p = v; ana->inop->inode.iso_mode = isonum_733(p->mode); ana->inop->inode.iso_uid = isonum_733(p->uid); ana->inop->inode.iso_gid = isonum_733(p->gid); ana->inop->inode.iso_links = isonum_733(p->links); ana->fields &= ~ISO_SUSP_ATTR; return (ISO_SUSP_ATTR); } static void cd9660_rrip_defattr(v, ana) void *v; ISO_RRIP_ANALYZE *ana; { struct iso_directory_record *isodir = v; /* But this is a required field! */ printf("RRIP without PX field?\n"); cd9660_defattr(isodir, ana->inop, NULL); } /* * Symbolic Links */ static int cd9660_rrip_slink(v, ana) void *v; ISO_RRIP_ANALYZE *ana; { ISO_RRIP_SLINK *p = v; register ISO_RRIP_SLINK_COMPONENT *pcomp; register ISO_RRIP_SLINK_COMPONENT *pcompe; int len, wlen, cont; char *outbuf, *inbuf; pcomp = (ISO_RRIP_SLINK_COMPONENT *)p->component; pcompe = (ISO_RRIP_SLINK_COMPONENT *)((char *)p + isonum_711(p->h.length)); len = *ana->outlen; outbuf = ana->outbuf; cont = ana->cont; /* * Gathering a Symbolic name from each component with path */ for (; pcomp < pcompe; pcomp = (ISO_RRIP_SLINK_COMPONENT *) ((char *)pcomp + ISO_RRIP_SLSIZ + isonum_711(pcomp->clen))) { if (!cont) { if (len < ana->maxlen) { len++; *outbuf++ = '/'; } } cont = 0; inbuf = ".."; wlen = 0; switch (*pcomp->cflag) { case ISO_SUSP_CFLAG_CURRENT: /* Inserting Current */ wlen = 1; break; case ISO_SUSP_CFLAG_PARENT: /* Inserting Parent */ wlen = 2; break; case ISO_SUSP_CFLAG_ROOT: /* Inserting slash for ROOT */ /* start over from beginning(?) */ outbuf -= len; len = 0; break; case ISO_SUSP_CFLAG_VOLROOT: /* Inserting a mount point i.e. "/cdrom" */ /* same as above */ outbuf -= len; len = 0; inbuf = ana->imp->im_mountp->mnt_stat.f_mntonname; wlen = strlen(inbuf); break; case ISO_SUSP_CFLAG_HOST: /* Inserting hostname i.e. "kurt.tools.de" */ inbuf = hostname; wlen = hostnamelen; break; case ISO_SUSP_CFLAG_CONTINUE: cont = 1; /* fall thru */ case 0: /* Inserting component */ wlen = isonum_711(pcomp->clen); inbuf = pcomp->name; break; default: printf("RRIP with incorrect flags?"); wlen = ana->maxlen + 1; break; } if (len + wlen > ana->maxlen) { /* indicate error to caller */ ana->cont = 1; ana->fields = 0; ana->outbuf -= *ana->outlen; *ana->outlen = 0; return (0); } bcopy(inbuf, outbuf, wlen); outbuf += wlen; len += wlen; } ana->outbuf = outbuf; *ana->outlen = len; ana->cont = cont; if (!isonum_711(p->flags)) { ana->fields &= ~ISO_SUSP_SLINK; return (ISO_SUSP_SLINK); } return (0); } /* * Alternate name */ static int cd9660_rrip_altname(v, ana) void *v; ISO_RRIP_ANALYZE *ana; { ISO_RRIP_ALTNAME *p = v; char *inbuf; int wlen; int cont; inbuf = ".."; wlen = 0; cont = 0; switch (*p->flags) { case ISO_SUSP_CFLAG_CURRENT: /* Inserting Current */ wlen = 1; break; case ISO_SUSP_CFLAG_PARENT: /* Inserting Parent */ wlen = 2; break; case ISO_SUSP_CFLAG_HOST: /* Inserting hostname i.e. "kurt.tools.de" */ inbuf = hostname; wlen = hostnamelen; break; case ISO_SUSP_CFLAG_CONTINUE: cont = 1; /* fall thru */ case 0: /* Inserting component */ wlen = isonum_711(p->h.length) - 5; inbuf = (char *)p + 5; break; default: printf("RRIP with incorrect NM flags?\n"); wlen = ana->maxlen + 1; break; } if ((*ana->outlen += wlen) > ana->maxlen) { /* treat as no name field */ ana->fields &= ~ISO_SUSP_ALTNAME; ana->outbuf -= *ana->outlen - wlen; *ana->outlen = 0; return (0); } bcopy(inbuf, ana->outbuf, wlen); ana->outbuf += wlen; if (!cont) { ana->fields &= ~ISO_SUSP_ALTNAME; return (ISO_SUSP_ALTNAME); } return (0); } static void cd9660_rrip_defname(v, ana) void *v; ISO_RRIP_ANALYZE *ana; { struct iso_directory_record *isodir = v; strlcpy(ana->outbuf, "..", ana->maxlen - *ana->outlen); switch (*isodir->name) { default: isofntrans(isodir->name, isonum_711(isodir->name_len), ana->outbuf, ana->outlen, 1, isonum_711(isodir->flags) & 4, ana->imp->joliet_level); break; case 0: *ana->outlen = 1; break; case 1: *ana->outlen = 2; break; } } /* * Parent or Child Link */ static int cd9660_rrip_pclink(v, ana) void *v; ISO_RRIP_ANALYZE *ana; { ISO_RRIP_CLINK *p = v; *ana->inump = isonum_733(p->dir_loc) << ana->imp->im_bshift; ana->fields &= ~(ISO_SUSP_CLINK | ISO_SUSP_PLINK); return (*p->h.type == 'C' ? ISO_SUSP_CLINK : ISO_SUSP_PLINK); } /* * Relocated directory */ /*ARGSUSED*/ static int cd9660_rrip_reldir(v, ana) void *v; ISO_RRIP_ANALYZE *ana; { /* special hack to make caller aware of RE field */ *ana->outlen = 0; ana->fields = 0; return (ISO_SUSP_RELDIR | ISO_SUSP_ALTNAME | ISO_SUSP_CLINK | ISO_SUSP_PLINK); } static int cd9660_rrip_tstamp(v, ana) void *v; ISO_RRIP_ANALYZE *ana; { ISO_RRIP_TSTAMP *p = v; u_char *ptime; ptime = p->time; /* Check a format of time stamp (7bytes/17bytes) */ if (!(*p->flags & ISO_SUSP_TSTAMP_FORM17)) { if (*p->flags & ISO_SUSP_TSTAMP_CREAT) ptime += 7; if (*p->flags & ISO_SUSP_TSTAMP_MODIFY) { cd9660_tstamp_conv7(ptime, &ana->inop->inode.iso_mtime); ptime += 7; } else bzero(&ana->inop->inode.iso_mtime, sizeof(struct timespec)); if (*p->flags & ISO_SUSP_TSTAMP_ACCESS) { cd9660_tstamp_conv7(ptime, &ana->inop->inode.iso_atime); ptime += 7; } else ana->inop->inode.iso_atime = ana->inop->inode.iso_mtime; if (*p->flags & ISO_SUSP_TSTAMP_ATTR) cd9660_tstamp_conv7(ptime, &ana->inop->inode.iso_ctime); else ana->inop->inode.iso_ctime = ana->inop->inode.iso_mtime; } else { if (*p->flags & ISO_SUSP_TSTAMP_CREAT) ptime += 17; if (*p->flags & ISO_SUSP_TSTAMP_MODIFY) { cd9660_tstamp_conv17(ptime, &ana->inop->inode.iso_mtime); ptime += 17; } else bzero(&ana->inop->inode.iso_mtime, sizeof(struct timespec)); if (*p->flags & ISO_SUSP_TSTAMP_ACCESS) { cd9660_tstamp_conv17(ptime, &ana->inop->inode.iso_atime); ptime += 17; } else ana->inop->inode.iso_atime = ana->inop->inode.iso_mtime; if (*p->flags & ISO_SUSP_TSTAMP_ATTR) cd9660_tstamp_conv17(ptime, &ana->inop->inode.iso_ctime); else ana->inop->inode.iso_ctime = ana->inop->inode.iso_mtime; } ana->fields &= ~ISO_SUSP_TSTAMP; return (ISO_SUSP_TSTAMP); } static void cd9660_rrip_deftstamp(v, ana) void *v; ISO_RRIP_ANALYZE *ana; { struct iso_directory_record *isodir = v; cd9660_deftstamp(isodir, ana->inop, NULL); } /* * POSIX device modes */ static int cd9660_rrip_device(v, ana) void *v; ISO_RRIP_ANALYZE *ana; { ISO_RRIP_DEVICE *p = v; u_int high, low; uint64_t tdev; high = isonum_733(p->dev_t_high); low = isonum_733(p->dev_t_low); tdev = (uint32_t)high; tdev <<= 32; tdev |= (uint32_t)low; ana->inop->inode.iso_rdev = (dev_t)tdev; ana->fields &= ~ISO_SUSP_DEVICE; return (ISO_SUSP_DEVICE); } /* * Flag indicating */ static int cd9660_rrip_idflag(v, ana) void *v; ISO_RRIP_ANALYZE *ana; { ISO_RRIP_IDFLAG *p = v; /* don't touch high bits */ ana->fields &= isonum_711(p->flags) | ~0xff; /* special handling of RE field */ if (ana->fields & ISO_SUSP_RELDIR) return (cd9660_rrip_reldir(p, ana)); return (ISO_SUSP_IDFLAG); } /* * Continuation pointer */ static int cd9660_rrip_cont(v, ana) void *v; ISO_RRIP_ANALYZE *ana; { ISO_RRIP_CONT *p = v; ana->iso_ce_blk = isonum_733(p->location); ana->iso_ce_off = isonum_733(p->offset); ana->iso_ce_len = isonum_733(p->length); return (ISO_SUSP_CONT); } /* * System Use end */ static int cd9660_rrip_stop(v, ana) void *v; ISO_RRIP_ANALYZE *ana; { return (ISO_SUSP_STOP); } /* * Extension reference */ static int cd9660_rrip_extref(v, ana) void *v; ISO_RRIP_ANALYZE *ana; { ISO_RRIP_EXTREF *p = v; if (isonum_711(p->version) != 1) return (0); if (isonum_711(p->len_id) != 9 && isonum_711(p->len_id) != 10) return (0); if (isonum_711(p->len_id) == 9 && bcmp((char *)p + 8, "IEEE_1282", 9)) return (0); if (isonum_711(p->len_id) == 10 && bcmp((char *)p + 8, "IEEE_P1282", 10) && bcmp((char *)p + 8, "RRIP_1991A", 10)) return (0); ana->fields &= ~ISO_SUSP_EXTREF; return (ISO_SUSP_EXTREF); } static int cd9660_rrip_loop(isodir, ana, table) struct iso_directory_record *isodir; ISO_RRIP_ANALYZE *ana; RRIP_TABLE *table; { register RRIP_TABLE *ptable; register ISO_SUSP_HEADER *phead; register ISO_SUSP_HEADER *pend; struct buf *bp = NULL; char *pwhead; u_char c; int result; /* * Note: If name length is odd, * it will be padded by 1 byte after the name */ pwhead = isodir->name + isonum_711(isodir->name_len); if (!(isonum_711(isodir->name_len) & 1)) pwhead++; isochar(isodir->name, pwhead, ana->imp->joliet_level, &c); /* If it's not the '.' entry of the root dir obey SP field */ if (c != 0 || isonum_733(isodir->extent) != ana->imp->root_extent) pwhead += ana->imp->rr_skip; else pwhead += ana->imp->rr_skip0; phead = (ISO_SUSP_HEADER *)pwhead; pend = (ISO_SUSP_HEADER *)((char *)isodir + isonum_711(isodir->length)); result = 0; while (1) { ana->iso_ce_len = 0; /* * Note: "pend" should be more than one SUSP header */ while (pend >= phead + 1) { if (isonum_711(phead->version) == 1) { for (ptable = table; ptable->func; ptable++) { if (*phead->type == *ptable->type && phead->type[1] == ptable->type[1]) { result |= ptable->func(phead, ana); break; } } if (!ana->fields) break; } if (result & ISO_SUSP_STOP) { result &= ~ISO_SUSP_STOP; break; } /* plausibility check */ if (isonum_711(phead->length) < sizeof(*phead)) break; /* * move to next SUSP * Hopefully this works with newer versions, too */ phead = (ISO_SUSP_HEADER *) ((char *)phead + isonum_711(phead->length)); } if (ana->fields && ana->iso_ce_len) { if (ana->iso_ce_blk >= ana->imp->volume_space_size || ana->iso_ce_off + ana->iso_ce_len > ana->imp->logical_block_size || bread(ana->imp->im_devvp, ana->iso_ce_blk << (ana->imp->im_bshift - DEV_BSHIFT), ana->imp->logical_block_size, NOCRED, &bp)) /* what to do now? */ break; phead = (ISO_SUSP_HEADER *)(bp->b_data + ana->iso_ce_off); pend = (ISO_SUSP_HEADER *) ((char *)phead + ana->iso_ce_len); } else break; } if (bp) brelse(bp); /* * If we don't find the Basic SUSP stuffs, just set default value * (attribute/time stamp) */ for (ptable = table; ptable->func2; ptable++) if (!(ptable->result & result)) ptable->func2(isodir, ana); return (result); } /* * Get Attributes. */ static RRIP_TABLE rrip_table_analyze[] = { { "PX", cd9660_rrip_attr, cd9660_rrip_defattr, ISO_SUSP_ATTR }, { "TF", cd9660_rrip_tstamp, cd9660_rrip_deftstamp, ISO_SUSP_TSTAMP }, { "PN", cd9660_rrip_device, 0, ISO_SUSP_DEVICE }, { "RR", cd9660_rrip_idflag, 0, ISO_SUSP_IDFLAG }, { "CE", cd9660_rrip_cont, 0, ISO_SUSP_CONT }, { "ST", cd9660_rrip_stop, 0, ISO_SUSP_STOP }, { "", 0, 0, 0 } }; int cd9660_rrip_analyze(isodir, inop, imp) struct iso_directory_record *isodir; struct iso_node *inop; struct iso_mnt *imp; { ISO_RRIP_ANALYZE analyze; analyze.inop = inop; analyze.imp = imp; analyze.fields = ISO_SUSP_ATTR | ISO_SUSP_TSTAMP | ISO_SUSP_DEVICE; return (cd9660_rrip_loop(isodir, &analyze, rrip_table_analyze)); } /* * Get Alternate Name. */ static RRIP_TABLE rrip_table_getname[] = { { "NM", cd9660_rrip_altname, cd9660_rrip_defname, ISO_SUSP_ALTNAME }, { "CL", cd9660_rrip_pclink, 0, ISO_SUSP_CLINK|ISO_SUSP_PLINK }, { "PL", cd9660_rrip_pclink, 0, ISO_SUSP_CLINK|ISO_SUSP_PLINK }, { "RE", cd9660_rrip_reldir, 0, ISO_SUSP_RELDIR }, { "RR", cd9660_rrip_idflag, 0, ISO_SUSP_IDFLAG }, { "CE", cd9660_rrip_cont, 0, ISO_SUSP_CONT }, { "ST", cd9660_rrip_stop, 0, ISO_SUSP_STOP }, { "", 0, 0, 0 } }; int cd9660_rrip_getname(isodir, outbuf, outlen, inump, imp) struct iso_directory_record *isodir; char *outbuf; u_short *outlen; ino_t *inump; struct iso_mnt *imp; { ISO_RRIP_ANALYZE analyze; RRIP_TABLE *tab; u_char c; analyze.outbuf = outbuf; analyze.outlen = outlen; analyze.maxlen = NAME_MAX; analyze.inump = inump; analyze.imp = imp; analyze.fields = ISO_SUSP_ALTNAME | ISO_SUSP_RELDIR | ISO_SUSP_CLINK | ISO_SUSP_PLINK; *outlen = 0; isochar(isodir->name, isodir->name + isonum_711(isodir->name_len), imp->joliet_level, &c); tab = rrip_table_getname; if (c == 0 || c == 1) { cd9660_rrip_defname(isodir, &analyze); analyze.fields &= ~ISO_SUSP_ALTNAME; tab++; } return (cd9660_rrip_loop(isodir, &analyze, tab)); } /* * Get Symbolic Link. */ static RRIP_TABLE rrip_table_getsymname[] = { { "SL", cd9660_rrip_slink, 0, ISO_SUSP_SLINK }, { "RR", cd9660_rrip_idflag, 0, ISO_SUSP_IDFLAG }, { "CE", cd9660_rrip_cont, 0, ISO_SUSP_CONT }, { "ST", cd9660_rrip_stop, 0, ISO_SUSP_STOP }, { "", 0, 0, 0 } }; int cd9660_rrip_getsymname(isodir, outbuf, outlen, imp) struct iso_directory_record *isodir; char *outbuf; u_short *outlen; struct iso_mnt *imp; { ISO_RRIP_ANALYZE analyze; analyze.outbuf = outbuf; analyze.outlen = outlen; *outlen = 0; analyze.maxlen = MAXPATHLEN; analyze.cont = 1; /* don't start with a slash */ analyze.imp = imp; analyze.fields = ISO_SUSP_SLINK; return (cd9660_rrip_loop(isodir, &analyze, rrip_table_getsymname) & ISO_SUSP_SLINK); } static RRIP_TABLE rrip_table_extref[] = { { "ER", cd9660_rrip_extref, 0, ISO_SUSP_EXTREF }, { "CE", cd9660_rrip_cont, 0, ISO_SUSP_CONT }, { "ST", cd9660_rrip_stop, 0, ISO_SUSP_STOP }, { "", 0, 0, 0 } }; /* * Check for Rock Ridge Extension and return offset of its fields. * Note: We insist on the ER field. */ int cd9660_rrip_offset(isodir, imp) struct iso_directory_record *isodir; struct iso_mnt *imp; { ISO_RRIP_OFFSET *p; ISO_RRIP_ANALYZE analyze; imp->rr_skip0 = 0; p = (ISO_RRIP_OFFSET *)(isodir->name + 1); if (bcmp(p, "SP\7\1\276\357", 6)) { /* Maybe, it's a CDROM XA disc? */ imp->rr_skip0 = 15; p = (ISO_RRIP_OFFSET *)((char *)p + 15); if (bcmp(p, "SP\7\1\276\357", 6)) return (-1); } analyze.imp = imp; analyze.fields = ISO_SUSP_EXTREF; if (!(cd9660_rrip_loop(isodir, &analyze, rrip_table_extref) & ISO_SUSP_EXTREF)) return (-1); return (isonum_711(p->skip)); } makefs/src/sys/isofs/cd9660/cd9660_rrip.h010064400000000000000000000106461020120115000150230ustar00/* $OpenBSD: cd9660_rrip.h,v 1.3 2003/06/02 23:28:05 millert Exp $ */ /* $NetBSD: cd9660_rrip.h,v 1.6 1994/12/13 22:33:24 mycroft Exp $ */ /*- * Copyright (c) 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. * * @(#)cd9660_rrip.h 8.2 (Berkeley) 12/5/94 */ typedef struct { char type [ISODCL ( 0, 1)]; u_char length [ISODCL ( 2, 2)]; /* 711 */ u_char version [ISODCL ( 3, 3)]; } ISO_SUSP_HEADER; typedef struct { ISO_SUSP_HEADER h; char mode [ISODCL ( 4, 11)]; /* 733 */ char links [ISODCL ( 12, 19)]; /* 733 */ char uid [ISODCL ( 20, 27)]; /* 733 */ char gid [ISODCL ( 28, 35)]; /* 733 */ } ISO_RRIP_ATTR; typedef struct { ISO_SUSP_HEADER h; char dev_t_high [ISODCL ( 4, 11)]; /* 733 */ char dev_t_low [ISODCL ( 12, 19)]; /* 733 */ } ISO_RRIP_DEVICE; #define ISO_SUSP_CFLAG_CONTINUE 0x01 #define ISO_SUSP_CFLAG_CURRENT 0x02 #define ISO_SUSP_CFLAG_PARENT 0x04 #define ISO_SUSP_CFLAG_ROOT 0x08 #define ISO_SUSP_CFLAG_VOLROOT 0x10 #define ISO_SUSP_CFLAG_HOST 0x20 typedef struct { u_char cflag [ISODCL ( 1, 1)]; u_char clen [ISODCL ( 2, 2)]; u_char name [1]; /* XXX */ } ISO_RRIP_SLINK_COMPONENT; #define ISO_RRIP_SLSIZ 2 typedef struct { ISO_SUSP_HEADER h; u_char flags [ISODCL ( 4, 4)]; u_char component [ISODCL ( 5, 5)]; } ISO_RRIP_SLINK; typedef struct { ISO_SUSP_HEADER h; char flags [ISODCL ( 4, 4)]; } ISO_RRIP_ALTNAME; typedef struct { ISO_SUSP_HEADER h; char dir_loc [ISODCL ( 4, 11)]; /* 733 */ } ISO_RRIP_CLINK; typedef struct { ISO_SUSP_HEADER h; char dir_loc [ISODCL ( 4, 11)]; /* 733 */ } ISO_RRIP_PLINK; typedef struct { ISO_SUSP_HEADER h; } ISO_RRIP_RELDIR; #define ISO_SUSP_TSTAMP_FORM17 0x80 #define ISO_SUSP_TSTAMP_FORM7 0x00 #define ISO_SUSP_TSTAMP_CREAT 0x01 #define ISO_SUSP_TSTAMP_MODIFY 0x02 #define ISO_SUSP_TSTAMP_ACCESS 0x04 #define ISO_SUSP_TSTAMP_ATTR 0x08 #define ISO_SUSP_TSTAMP_BACKUP 0x10 #define ISO_SUSP_TSTAMP_EXPIRE 0x20 #define ISO_SUSP_TSTAMP_EFFECT 0x40 typedef struct { ISO_SUSP_HEADER h; u_char flags [ISODCL ( 4, 4)]; u_char time [ISODCL ( 5, 5)]; } ISO_RRIP_TSTAMP; typedef struct { ISO_SUSP_HEADER h; u_char flags [ISODCL ( 4, 4)]; } ISO_RRIP_IDFLAG; typedef struct { ISO_SUSP_HEADER h; char len_id [ISODCL ( 4, 4)]; char len_des [ISODCL ( 5, 5)]; char len_src [ISODCL ( 6, 6)]; char version [ISODCL ( 7, 7)]; } ISO_RRIP_EXTREF; typedef struct { ISO_SUSP_HEADER h; char check [ISODCL ( 4, 5)]; char skip [ISODCL ( 6, 6)]; } ISO_RRIP_OFFSET; typedef struct { ISO_SUSP_HEADER h; char location [ISODCL ( 4, 11)]; char offset [ISODCL ( 12, 19)]; char length [ISODCL ( 20, 27)]; } ISO_RRIP_CONT; makefs/src/sys/isofs/cd9660/cd9660_util.c010064400000000000000000000123651020120115000150170ustar00/* $OpenBSD: cd9660_util.c,v 1.7 2003/11/04 21:54:01 mickey Exp $ */ /* $NetBSD: cd9660_util.c,v 1.12 1997/01/24 00:27:33 cgd Exp $ */ /*- * Copyright (c) 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). Joliet support was added by * Joachim Kuebart (joki@kuebart.stuttgart.netsurf.de). * * 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. * * @(#)cd9660_util.c 8.3 (Berkeley) 12/5/94 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * XXX: limited support for loading of Unicode * conversion routine as a kld at a run-time. * Should be removed when native Unicode kernel * interfaces have been introduced. */ u_char (*cd9660_wchar2char)(u_int32_t wchar) = NULL; /* * Get one character out of an iso filename * Obey joliet_level * Return number of bytes consumed */ int isochar(isofn, isoend, joliet_level, c) const u_char *isofn; const u_char *isoend; int joliet_level; u_char *c; { *c = *isofn++; if (joliet_level == 0 || isofn == isoend) /* (00) and (01) are one byte in Joliet, too */ return 1; /* No Unicode support yet :-( */ switch (*c) { default: *c = '?'; break; case '\0': *c = *isofn; break; } /* XXX: if Unicode conversion routine is loaded then use it */ if (cd9660_wchar2char != NULL) *c = cd9660_wchar2char((*(isofn - 1) << 8) | *isofn); return 2; } /* * translate and compare a filename * returns (fn - isofn) * Note: Version number plus ';' may be omitted. */ int isofncmp(fn, fnlen, isofn, isolen, joliet_level) const u_char *fn, *isofn; int fnlen, isolen, joliet_level; { int i, j; u_char c; const u_char *fnend = fn + fnlen, *isoend = isofn + isolen; for (; fn != fnend; fn++) { if (isofn == isoend) return *fn; isofn += isochar(isofn, isoend, joliet_level, &c); if (c == ';') { if (*fn++ != ';') return fn[-1]; for (i = 0; fn != fnend; i = i * 10 + *fn++ - '0') { if (*fn < '0' || *fn > '9') { return -1; } } for (j = 0; isofn != isoend; j = j * 10 + c - '0') isofn += isochar(isofn, isoend, joliet_level, &c); return i - j; } if (((u_char) c) != *fn) { if (c >= 'A' && c <= 'Z') { if (c + ('a' - 'A') != *fn) { if (*fn >= 'a' && *fn <= 'z') return *fn - ('a' - 'A') - c; else return *fn - c; } } else return *fn - c; } } if (isofn != isoend) { isofn += isochar(isofn, isoend, joliet_level, &c); switch (c) { default: return -c; case '.': if (isofn != isoend) { isochar(isofn, isoend, joliet_level, &c); if (c == ';') return 0; } return -1; case ';': return 0; } } return 0; } /* * translate a filename of length > 0 */ void isofntrans(infn, infnlen, outfn, outfnlen, original, assoc, joliet_level) u_char *infn, *outfn; int infnlen; u_short *outfnlen; int original; int assoc; int joliet_level; { int fnidx = 0; u_char c, d = '\0', *infnend = infn + infnlen; if (assoc) { *outfn++ = ASSOCCHAR; fnidx++; } for (; infn != infnend; fnidx++) { infn += isochar(infn, infnend, joliet_level, &c); if (!original && c == ';') { fnidx -= (d == '.'); break; } else *outfn++ = c; d = c; } *outfnlen = fnidx; } makefs/src/sys/isofs/cd9660/cd9660_vfsops.c010064400000000000000000000566451341414421600154150ustar00/* $OpenBSD: cd9660_vfsops.c,v 1.35 2003/08/14 07:46:39 mickey Exp $ */ /* $NetBSD: cd9660_vfsops.c,v 1.26 1997/06/13 15:38:58 pk Exp $ */ /*- * Copyright (c) 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. * * @(#)cd9660_vfsops.c 8.9 (Berkeley) 12/5/94 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __GLIBC__ #include #endif #define b_cylin b_resid #include #include #include #include const struct vfsops cd9660_vfsops = { cd9660_mount, cd9660_start, cd9660_unmount, cd9660_root, cd9660_quotactl, cd9660_statfs, cd9660_sync, cd9660_vget, cd9660_fhtovp, cd9660_vptofh, cd9660_init, cd9660_sysctl, cd9660_check_export }; /* * Called by vfs_mountroot when iso is going to be mounted as root. */ static int iso_mountfs(struct vnode *devvp, struct mount *mp, struct proc *p, struct iso_args *argp); int iso_disklabelspoof(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp); int cd9660_mountroot() { struct mount *mp; extern struct vnode *rootvp; struct proc *p = curproc; /* XXX */ int error; struct iso_args args; /* * Get vnodes for swapdev and rootdev. */ if ((error = bdevvp(swapdev, &swapdev_vp)) || (error = bdevvp(rootdev, &rootvp))) { printf("cd9660_mountroot: can't setup bdevvp's"); return (error); } if ((error = vfs_rootmountalloc("cd9660", "root_device", &mp)) != 0) return (error); args.flags = ISOFSMNT_ROOT; if ((error = iso_mountfs(rootvp, mp, p, &args)) != 0) { mp->mnt_vfc->vfc_refcount--; vfs_unbusy(mp, p); free(mp, M_MOUNT); return (error); } simple_lock(&mountlist_slock); CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list); simple_unlock(&mountlist_slock); (void)cd9660_statfs(mp, &mp->mnt_stat, p); vfs_unbusy(mp, p); inittodr(0); return (0); } /* * VFS Operations. * * mount system call */ int cd9660_mount(mp, path, data, ndp, p) register struct mount *mp; const char *path; void *data; struct nameidata *ndp; struct proc *p; { struct vnode *devvp; struct iso_args args; size_t size; int error; struct iso_mnt *imp = NULL; error = copyin(data, &args, sizeof (struct iso_args)); if (error) return (error); if ((mp->mnt_flag & MNT_RDONLY) == 0) return (EROFS); /* * If updating, check whether changing from read-only to * read/write; if there is no device name, that's all we do. */ if (mp->mnt_flag & MNT_UPDATE) { imp = VFSTOISOFS(mp); if (args.fspec == 0) return (vfs_export(mp, &imp->im_export, &args.export_info)); } /* * Not an update, or updating the name: look up the name * and verify that it refers to a sensible block device. */ NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p); if ((error = namei(ndp)) != 0) return (error); devvp = ndp->ni_vp; if (devvp->v_type != VBLK) { vrele(devvp); return (ENOTBLK); } if (major(devvp->v_rdev) >= nblkdev) { vrele(devvp); return (ENXIO); } /* * If mount by non-root, then verify that user has necessary * permissions on the device. */ if (p->p_ucred->cr_uid != 0) { vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p); error = VOP_ACCESS(devvp, VREAD, p->p_ucred, p); if (error) { vput(devvp); return (error); } VOP_UNLOCK(devvp, 0, p); } if ((mp->mnt_flag & MNT_UPDATE) == 0) error = iso_mountfs(devvp, mp, p, &args); else { if (devvp != imp->im_devvp) error = EINVAL; /* needs translation */ else vrele(devvp); } if (error) { vrele(devvp); return (error); } imp = VFSTOISOFS(mp); (void)copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); (void)copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); bcopy(&args, &mp->mnt_stat.mount_info.iso_args, sizeof(args)); (void)cd9660_statfs(mp, &mp->mnt_stat, p); return (0); } /* * Common code for mount and mountroot */ static int iso_mountfs(devvp, mp, p, argp) register struct vnode *devvp; struct mount *mp; struct proc *p; struct iso_args *argp; { register struct iso_mnt *isomp = (struct iso_mnt *)0; struct buf *bp = NULL; struct buf *pribp = NULL, *supbp = NULL; dev_t dev = devvp->v_rdev; int error = EINVAL; int needclose = 0; int ronly = (mp->mnt_flag & MNT_RDONLY) != 0; extern struct vnode *rootvp; int iso_bsize; int iso_blknum; int joliet_level; struct iso_volume_descriptor *vdp; struct iso_primary_descriptor *pri = NULL; struct iso_supplementary_descriptor *sup = NULL; struct iso_directory_record *rootp; int logical_block_size; int sess = 0; if (!ronly) return (EROFS); /* * Disallow multiple mounts of the same device. * Disallow mounting of a device that is currently in use * (except for root, which might share swap device for miniroot). * Flush out any old buffers remaining from a previous use. */ if ((error = vfs_mountedon(devvp)) != 0) return (error); if (vcount(devvp) > 1 && devvp != rootvp) return (EBUSY); if ((error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0)) != 0) return (error); error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p); if (error) return (error); needclose = 1; /* This is the "logical sector size". The standard says this * should be 2048 or the physical sector size on the device, * whichever is greater. For now, we'll just use a constant. */ iso_bsize = ISO_DEFAULT_BLOCK_SIZE; error = VOP_IOCTL(devvp, CDIOREADMSADDR, (caddr_t)&sess, 0, FSCRED, p); if (error) sess = 0; joliet_level = 0; for (iso_blknum = 16; iso_blknum < 100; iso_blknum++) { if ((error = bread(devvp, (iso_blknum + sess) * btodb(iso_bsize), iso_bsize, NOCRED, &bp)) != 0) goto out; vdp = (struct iso_volume_descriptor *)bp->b_data; if (bcmp (vdp->id, ISO_STANDARD_ID, sizeof vdp->id) != 0) { error = EINVAL; goto out; } switch (isonum_711 (vdp->type)){ case ISO_VD_PRIMARY: if (pribp == NULL) { pribp = bp; bp = NULL; pri = (struct iso_primary_descriptor *)vdp; } break; case ISO_VD_SUPPLEMENTARY: if (supbp == NULL) { supbp = bp; bp = NULL; sup = (struct iso_supplementary_descriptor *)vdp; if (!(argp->flags & ISOFSMNT_NOJOLIET)) { if (bcmp(sup->escape, "%/@", 3) == 0) joliet_level = 1; if (bcmp(sup->escape, "%/C", 3) == 0) joliet_level = 2; if (bcmp(sup->escape, "%/E", 3) == 0) joliet_level = 3; if (isonum_711 (sup->flags) & 1) joliet_level = 0; } } break; case ISO_VD_END: goto vd_end; default: break; } if (bp) { brelse(bp); bp = NULL; } } vd_end: if (bp) { brelse(bp); bp = NULL; } if (pri == NULL) { error = EINVAL; goto out; } logical_block_size = isonum_723 (pri->logical_block_size); if (logical_block_size < DEV_BSIZE || logical_block_size > MAXBSIZE || (logical_block_size & (logical_block_size - 1)) != 0) { error = EINVAL; goto out; } rootp = (struct iso_directory_record *)pri->root_directory_record; isomp = malloc(sizeof *isomp, M_ISOFSMNT, M_WAITOK); bzero((caddr_t)isomp, sizeof *isomp); isomp->logical_block_size = logical_block_size; isomp->volume_space_size = isonum_733 (pri->volume_space_size); bcopy (rootp, isomp->root, sizeof isomp->root); isomp->root_extent = isonum_733 (rootp->extent); isomp->root_size = isonum_733 (rootp->size); isomp->joliet_level = 0; isomp->im_bmask = logical_block_size - 1; isomp->im_bshift = ffs(logical_block_size) - 1; pribp->b_flags |= B_AGE; brelse(pribp); pribp = NULL; mp->mnt_data = (qaddr_t)isomp; mp->mnt_stat.f_fsid.val[0] = (long)dev; mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; mp->mnt_maxsymlinklen = 0; mp->mnt_flag |= MNT_LOCAL; isomp->im_mountp = mp; isomp->im_dev = dev; isomp->im_devvp = devvp; devvp->v_specmountpoint = mp; /* Check the Rock Ridge Extention support */ if (!(argp->flags & ISOFSMNT_NORRIP)) { if ((error = bread(isomp->im_devvp, (isomp->root_extent + isonum_711(rootp->ext_attr_length)) << (isomp->im_bshift - DEV_BSHIFT), isomp->logical_block_size, NOCRED, &bp)) != 0) goto out; rootp = (struct iso_directory_record *)bp->b_data; if ((isomp->rr_skip = cd9660_rrip_offset(rootp,isomp)) < 0) { argp->flags |= ISOFSMNT_NORRIP; } else { argp->flags &= ~ISOFSMNT_GENS; } /* * The contents are valid, * but they will get reread as part of another vnode, so... */ bp->b_flags |= B_AGE; brelse(bp); bp = NULL; } isomp->im_flags = argp->flags & (ISOFSMNT_NORRIP | ISOFSMNT_GENS | ISOFSMNT_EXTATT | ISOFSMNT_NOJOLIET); switch (isomp->im_flags & (ISOFSMNT_NORRIP | ISOFSMNT_GENS)) { default: isomp->iso_ftype = ISO_FTYPE_DEFAULT; break; case ISOFSMNT_GENS|ISOFSMNT_NORRIP: isomp->iso_ftype = ISO_FTYPE_9660; break; case 0: isomp->iso_ftype = ISO_FTYPE_RRIP; break; } /* Decide whether to use the Joliet descriptor */ if (isomp->iso_ftype != ISO_FTYPE_RRIP && joliet_level) { rootp = (struct iso_directory_record *) sup->root_directory_record; bcopy(rootp, isomp->root, sizeof isomp->root); isomp->root_extent = isonum_733(rootp->extent); isomp->root_size = isonum_733(rootp->size); isomp->joliet_level = joliet_level; supbp->b_flags |= B_AGE; } if (supbp) { brelse(supbp); supbp = NULL; } return (0); out: if (bp) brelse(bp); if (supbp) brelse(supbp); if (needclose) (void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, NOCRED, p); if (isomp) { free((caddr_t)isomp, M_ISOFSMNT); mp->mnt_data = (qaddr_t)0; } return (error); } /* * Test to see if the device is an ISOFS filesystem. */ int iso_disklabelspoof(dev, strat, lp) dev_t dev; void (*strat)(struct buf *); register struct disklabel *lp; { struct buf *bp = NULL; struct iso_volume_descriptor *vdp; struct iso_primary_descriptor *pri; int logical_block_size; int error = EINVAL; int iso_blknum; int i; bp = geteblk(ISO_DEFAULT_BLOCK_SIZE); bp->b_dev = dev; for (iso_blknum = 16; iso_blknum < 100; iso_blknum++) { bp->b_blkno = iso_blknum * btodb(ISO_DEFAULT_BLOCK_SIZE); bp->b_bcount = ISO_DEFAULT_BLOCK_SIZE; bp->b_flags = B_BUSY | B_READ; bp->b_cylin = bp->b_blkno / lp->d_secpercyl; /*printf("d_secsize %d iso_blknum %d b_blkno %d bcount %d\n", lp->d_secsize, iso_blknum, bp->b_blkno, bp->b_bcount);*/ (*strat)(bp); if (biowait(bp)) goto out; vdp = (struct iso_volume_descriptor *)bp->b_data; /*printf("%2x%2x%2x type %2x\n", vdp->id[0], vdp->id[1], vdp->id[2], isonum_711(vdp->type));*/ if (bcmp (vdp->id, ISO_STANDARD_ID, sizeof vdp->id) != 0 || isonum_711 (vdp->type) == ISO_VD_END) goto out; if (isonum_711 (vdp->type) == ISO_VD_PRIMARY) break; } if (isonum_711 (vdp->type) != ISO_VD_PRIMARY) goto out; pri = (struct iso_primary_descriptor *)vdp; logical_block_size = isonum_723 (pri->logical_block_size); if (logical_block_size < DEV_BSIZE || logical_block_size > MAXBSIZE || (logical_block_size & (logical_block_size - 1)) != 0) goto out; /* * build a disklabel for the CD */ strncpy(lp->d_typename, pri->volume_id, sizeof lp->d_typename); strncpy(lp->d_packname, pri->volume_id+16, sizeof lp->d_packname); for (i = 0; i < MAXPARTITIONS; i++) { lp->d_partitions[i].p_size = 0; lp->d_partitions[i].p_offset = 0; } lp->d_partitions[0].p_offset = 0; lp->d_partitions[0].p_size = lp->d_secperunit; lp->d_partitions[0].p_fstype = FS_ISO9660; lp->d_partitions[RAW_PART].p_offset = 0; lp->d_partitions[RAW_PART].p_size = lp->d_secperunit; lp->d_partitions[RAW_PART].p_fstype = FS_ISO9660; lp->d_npartitions = RAW_PART + 1; lp->d_bbsize = 8192; /* fake */ lp->d_sbsize = 64*1024; /* fake */ lp->d_magic = DISKMAGIC; lp->d_magic2 = DISKMAGIC; lp->d_checksum = dkcksum(lp); error = 0; out: bp->b_flags |= B_INVAL; brelse(bp); return (error); } /* * Make a filesystem operational. * Nothing to do at the moment. */ /* ARGSUSED */ int cd9660_start(mp, flags, p) struct mount *mp; int flags; struct proc *p; { return (0); } /* * unmount system call */ int cd9660_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { register struct iso_mnt *isomp; int error, flags = 0; if (mntflags & MNT_FORCE) flags |= FORCECLOSE; #if 0 mntflushbuf(mp, 0); if (mntinvalbuf(mp)) return (EBUSY); #endif if ((error = vflush(mp, NULLVP, flags)) != 0) return (error); isomp = VFSTOISOFS(mp); #ifdef ISODEVMAP if (isomp->iso_ftype == ISO_FTYPE_RRIP) iso_dunmap(isomp->im_dev); #endif isomp->im_devvp->v_specmountpoint = NULL; error = VOP_CLOSE(isomp->im_devvp, FREAD, NOCRED, p); vrele(isomp->im_devvp); free((caddr_t)isomp, M_ISOFSMNT); mp->mnt_data = (qaddr_t)0; mp->mnt_flag &= ~MNT_LOCAL; return (error); } /* * Return root of a filesystem */ int cd9660_root(mp, vpp) struct mount *mp; struct vnode **vpp; { struct iso_mnt *imp = VFSTOISOFS(mp); struct iso_directory_record *dp = (struct iso_directory_record *)imp->root; ino_t ino = isodirino(dp, imp); /* * With RRIP we must use the `.' entry of the root directory. * Simply tell vget, that it's a relocated directory. */ return (cd9660_vget_internal(mp, ino, vpp, imp->iso_ftype == ISO_FTYPE_RRIP, dp)); } /* * Do operations associated with quotas, not supported */ /* ARGSUSED */ int cd9660_quotactl(mp, cmd, uid, arg, p) struct mount *mp; int cmd; uid_t uid; caddr_t arg; struct proc *p; { return (EOPNOTSUPP); } /* * Get filesystem statistics. */ int cd9660_statfs(mp, sbp, p) struct mount *mp; register struct statfs *sbp; struct proc *p; { register struct iso_mnt *isomp; isomp = VFSTOISOFS(mp); sbp->f_bsize = isomp->logical_block_size; sbp->f_iosize = sbp->f_bsize; /* XXX */ sbp->f_blocks = isomp->volume_space_size; sbp->f_bfree = 0; /* total free blocks */ sbp->f_bavail = 0; /* blocks free for non superuser */ sbp->f_files = 0; /* total files */ sbp->f_ffree = 0; /* free file nodes */ if (sbp != &mp->mnt_stat) { bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); bcopy(&mp->mnt_stat.mount_info.iso_args, &sbp->mount_info.iso_args, sizeof(struct iso_args)); } /* Use the first spare for flags: */ sbp->f_spare[0] = isomp->im_flags; return (0); } /* ARGSUSED */ int cd9660_sync(mp, waitfor, cred, p) struct mount *mp; int waitfor; struct ucred *cred; struct proc *p; { return (0); } /* * File handle to vnode * * Have to be really careful about stale file handles: * - check that the inode number is in range * - call iget() to get the locked inode * - check for an unallocated inode (i_mode == 0) * - check that the generation number matches */ struct ifid { ushort ifid_len; ushort ifid_pad; int ifid_ino; long ifid_start; }; /* ARGSUSED */ int cd9660_fhtovp(mp, fhp, vpp) register struct mount *mp; struct fid *fhp; struct vnode **vpp; { struct ifid *ifhp = (struct ifid *)fhp; register struct iso_node *ip; struct vnode *nvp; int error; #ifdef ISOFS_DBG printf("fhtovp: ino %d, start %ld\n", ifhp->ifid_ino, ifhp->ifid_start); #endif if ((error = VFS_VGET(mp, ifhp->ifid_ino, &nvp)) != 0) { *vpp = NULLVP; return (error); } ip = VTOI(nvp); if (ip->inode.iso_mode == 0) { vput(nvp); *vpp = NULLVP; return (ESTALE); } *vpp = nvp; return (0); } int cd9660_vget(mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { /* * XXXX * It would be nice if we didn't always set the `relocated' flag * and force the extra read, but I don't want to think about fixing * that right now. */ return (cd9660_vget_internal(mp, ino, vpp, #if 0 VFSTOISOFS(mp)->iso_ftype == ISO_FTYPE_RRIP, #else 0, #endif NULL)); } int cd9660_vget_internal(mp, ino, vpp, relocated, isodir) struct mount *mp; ino_t ino; struct vnode **vpp; int relocated; struct iso_directory_record *isodir; { register struct iso_mnt *imp; struct iso_node *ip; struct buf *bp; struct vnode *vp, *nvp; dev_t dev; int error; retry: imp = VFSTOISOFS(mp); dev = imp->im_dev; if ((*vpp = cd9660_ihashget(dev, ino)) != NULLVP) return (0); /* Allocate a new vnode/iso_node. */ if ((error = getnewvnode(VT_ISOFS, mp, cd9660_vnodeop_p, &vp)) != 0) { *vpp = NULLVP; return (error); } MALLOC(ip, struct iso_node *, sizeof(struct iso_node), M_ISOFSNODE, M_WAITOK); bzero((caddr_t)ip, sizeof(struct iso_node)); lockinit(&ip->i_lock, PINOD, "isoinode", 0, 0); vp->v_data = ip; ip->i_vnode = vp; ip->i_dev = dev; ip->i_number = ino; /* * Put it onto its hash chain and lock it so that other requests for * this inode will block if they arrive while we are sleeping waiting * for old data structures to be purged or for the contents of the * disk portion of this inode to be read. */ error = cd9660_ihashins(ip); if (error) { vrele(vp); if (error == EEXIST) goto retry; return (error); } if (isodir == 0) { int lbn, off; lbn = lblkno(imp, ino); if (lbn >= imp->volume_space_size) { vput(vp); printf("fhtovp: lbn exceed volume space %d\n", lbn); return (ESTALE); } off = blkoff(imp, ino); if (off + ISO_DIRECTORY_RECORD_SIZE > imp->logical_block_size) { vput(vp); printf("fhtovp: crosses block boundary %d\n", off + ISO_DIRECTORY_RECORD_SIZE); return (ESTALE); } error = bread(imp->im_devvp, lbn << (imp->im_bshift - DEV_BSHIFT), imp->logical_block_size, NOCRED, &bp); if (error) { vput(vp); brelse(bp); printf("fhtovp: bread error %d\n",error); return (error); } isodir = (struct iso_directory_record *)(bp->b_data + off); if (off + isonum_711(isodir->length) > imp->logical_block_size) { vput(vp); if (bp != 0) brelse(bp); printf("fhtovp: directory crosses block boundary %d[off=%d/len=%d]\n", off +isonum_711(isodir->length), off, isonum_711(isodir->length)); return (ESTALE); } #if 0 if (isonum_733(isodir->extent) + isonum_711(isodir->ext_attr_length) != ifhp->ifid_start) { if (bp != 0) brelse(bp); printf("fhtovp: file start miss %d vs %d\n", isonum_733(isodir->extent) + isonum_711(isodir->ext_attr_length), ifhp->ifid_start); return (ESTALE); } #endif } else bp = 0; ip->i_mnt = imp; ip->i_devvp = imp->im_devvp; VREF(ip->i_devvp); if (relocated) { /* * On relocated directories we must * read the `.' entry out of a dir. */ ip->iso_start = ino >> imp->im_bshift; if (bp != 0) brelse(bp); if ((error = cd9660_bufatoff(ip, (off_t)0, NULL, &bp)) != 0) { vput(vp); return (error); } isodir = (struct iso_directory_record *)bp->b_data; } ip->iso_extent = isonum_733(isodir->extent); ip->i_size = isonum_733(isodir->size); ip->iso_start = isonum_711(isodir->ext_attr_length) + ip->iso_extent; /* * Setup time stamp, attribute */ vp->v_type = VNON; switch (imp->iso_ftype) { default: /* ISO_FTYPE_9660 */ { struct buf *bp2; int off; if ((imp->im_flags & ISOFSMNT_EXTATT) && (off = isonum_711(isodir->ext_attr_length))) cd9660_bufatoff(ip, (off_t)-(off << imp->im_bshift), NULL, &bp2); else bp2 = NULL; cd9660_defattr(isodir, ip, bp2); cd9660_deftstamp(isodir, ip, bp2); if (bp2) brelse(bp2); break; } case ISO_FTYPE_RRIP: cd9660_rrip_analyze(isodir, ip, imp); break; } if (bp != 0) brelse(bp); /* * Initialize the associated vnode */ switch (vp->v_type = IFTOVT(ip->inode.iso_mode)) { case VFIFO: #ifdef FIFO vp->v_op = cd9660_fifoop_p; break; #else vput(vp); return (EOPNOTSUPP); #endif /* FIFO */ case VCHR: case VBLK: /* * if device, look at device number table for translation */ #ifdef ISODEVMAP if (dp = iso_dmap(dev, ino, 0)) ip->inode.iso_rdev = dp->d_dev; #endif vp->v_op = cd9660_specop_p; if ((nvp = checkalias(vp, ip->inode.iso_rdev, mp)) != NULL) { /* * Discard unneeded vnode, but save its iso_node. * Note that the lock is carried over in the iso_node */ nvp->v_data = vp->v_data; vp->v_data = NULL; vp->v_op = spec_vnodeop_p; vrele(vp); vgone(vp); /* * Reinitialize aliased inode. */ vp = nvp; ip->i_vnode = vp; } break; case VLNK: case VNON: case VSOCK: case VDIR: case VBAD: break; case VREG: uvm_vnp_setsize(vp, ip->i_size); break; } if (ip->iso_extent == imp->root_extent) vp->v_flag |= VROOT; /* * XXX need generation number? */ *vpp = vp; return (0); } /* * Vnode pointer to File handle */ /* ARGSUSED */ int cd9660_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { register struct iso_node *ip = VTOI(vp); register struct ifid *ifhp; ifhp = (struct ifid *)fhp; ifhp->ifid_len = sizeof(struct ifid); ifhp->ifid_ino = ip->i_number; ifhp->ifid_start = ip->iso_start; #ifdef ISOFS_DBG printf("vptofh: ino %d, start %ld\n", ifhp->ifid_ino,ifhp->ifid_start); #endif return (0); } /* * Verify a remote client has export rights and return these rights via * exflagsp and credanonp. */ int cd9660_check_export(mp, nam, exflagsp, credanonp) register struct mount *mp; struct mbuf *nam; int *exflagsp; struct ucred **credanonp; { register struct netcred *np; register struct iso_mnt *imp = VFSTOISOFS(mp); /* * Get the export permission structure for this tuple. */ np = vfs_export_lookup(mp, &imp->im_export, nam); if (np == NULL) return (EACCES); *exflagsp = np->netc_exflags; *credanonp = &np->netc_anon; return (0); } makefs/src/sys/isofs/cd9660/cd9660_vnops.c010064400000000000000000000631011341415427300152270ustar00/* $OpenBSD: cd9660_vnops.c,v 1.32 2004/11/29 17:05:05 grange Exp $ */ /* $NetBSD: cd9660_vnops.c,v 1.42 1997/10/16 23:56:57 christos Exp $ */ /*- * Copyright (c) 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. * * @(#)cd9660_vnops.c 8.15 (Berkeley) 12/5/94 */ #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 GNUPORT extern size_t strlcpy(char *, const char *, size_t); #endif /* * Structure for reading directories */ struct isoreaddir { struct dirent saveent; struct dirent assocent; struct dirent current; off_t saveoff; off_t assocoff; off_t curroff; struct uio *uio; off_t uio_off; int eofflag; u_long *cookies; int ncookies; }; int iso_uiodir(struct isoreaddir *, struct dirent *, off_t); int iso_shipdir(struct isoreaddir *); #if 0 /* * Mknod vnode call * Actually remap the device number */ int cd9660_mknod(ndp, vap, cred, p) struct nameidata *ndp; struct ucred *cred; struct vattr *vap; struct proc *p; { #ifndef ISODEVMAP pool_put(&namei_pool, ndp->ni_pnbuf); vput(ndp->ni_dvp); vput(ndp->ni_vp); return (EINVAL); #else register struct vnode *vp; struct iso_node *ip; struct iso_dnode *dp; int error; vp = ndp->ni_vp; ip = VTOI(vp); if (ip->i_mnt->iso_ftype != ISO_FTYPE_RRIP || vap->va_type != vp->v_type || (vap->va_type != VCHR && vap->va_type != VBLK)) { pool_put(&namei_pool, ndp->ni_pnbuf); vput(ndp->ni_dvp); vput(ndp->ni_vp); return (EINVAL); } dp = iso_dmap(ip->i_dev,ip->i_number,1); if (ip->inode.iso_rdev == vap->va_rdev || vap->va_rdev == VNOVAL) { /* same as the unmapped one, delete the mapping */ remque(dp); FREE(dp, M_CACHE); } else /* enter new mapping */ dp->d_dev = vap->va_rdev; /* * Remove inode so that it will be reloaded by iget and * checked to see if it is an alias of an existing entry * in the inode cache. */ vput(vp); vp->v_type = VNON; vgone(vp); return (0); #endif } #endif /* * Setattr call. Only allowed for block and character special devices. */ int cd9660_setattr(v) void *v; { struct vop_setattr_args /* { struct vnodeop_desc *a_desc; struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap = v; struct vnode *vp = ap->a_vp; struct vattr *vap = ap->a_vap; if (vap->va_flags != VNOVAL || vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL) return (EROFS); if (vap->va_size != VNOVAL) { switch (vp->v_type) { case VDIR: return (EISDIR); case VLNK: case VREG: return (EROFS); case VCHR: case VBLK: case VSOCK: case VFIFO: return (0); default: return (EINVAL); } } return (EINVAL); } /* * Open called. * * Nothing to do. */ /* ARGSUSED */ int cd9660_open(v) void *v; { return (0); } /* * Close called * * Update the times on the inode on writeable filesystems. */ /* ARGSUSED */ int cd9660_close(v) void *v; { return (0); } /* * Check mode permission on inode pointer. Mode is READ, WRITE or EXEC. * The mode is shifted to select the owner/group/other fields. The * super user is granted all permissions. */ int cd9660_access(v) void *v; { struct vop_access_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap = v; struct iso_node *ip = VTOI(ap->a_vp); return (vaccess(ip->inode.iso_mode & ALLPERMS, ip->inode.iso_uid, ip->inode.iso_gid, ap->a_mode, ap->a_cred)); } int cd9660_getattr(v) void *v; { struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap = v; struct vnode *vp = ap->a_vp; register struct vattr *vap = ap->a_vap; register struct iso_node *ip = VTOI(vp); vap->va_fsid = ip->i_dev; vap->va_fileid = ip->i_number; vap->va_mode = ip->inode.iso_mode & ALLPERMS; vap->va_nlink = ip->inode.iso_links; vap->va_uid = ip->inode.iso_uid; vap->va_gid = ip->inode.iso_gid; vap->va_atime = ip->inode.iso_atime; vap->va_mtime = ip->inode.iso_mtime; vap->va_ctime = ip->inode.iso_ctime; vap->va_rdev = ip->inode.iso_rdev; vap->va_size = (u_quad_t) ip->i_size; if (ip->i_size == 0 && vp->v_type == VLNK) { struct vop_readlink_args rdlnk; struct iovec aiov; struct uio auio; char *cp; MALLOC(cp, char *, MAXPATHLEN, M_TEMP, M_WAITOK); aiov.iov_base = cp; aiov.iov_len = MAXPATHLEN; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = 0; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_SYSSPACE; auio.uio_procp = ap->a_p; auio.uio_resid = MAXPATHLEN; rdlnk.a_uio = &auio; rdlnk.a_vp = ap->a_vp; rdlnk.a_cred = ap->a_cred; if (cd9660_readlink(&rdlnk) == 0) vap->va_size = MAXPATHLEN - auio.uio_resid; FREE(cp, M_TEMP); } vap->va_flags = 0; vap->va_gen = 1; vap->va_blocksize = ip->i_mnt->logical_block_size; vap->va_bytes = (u_quad_t) ip->i_size; vap->va_type = vp->v_type; return (0); } #ifdef DEBUG extern int doclusterread; #else #define doclusterread 1 #endif /* XXX until cluster routines can handle block sizes less than one page */ #define cd9660_doclusterread \ (doclusterread && (ISO_DEFAULT_BLOCK_SIZE >= NBPG)) /* * Vnode op for reading. */ int cd9660_read(v) void *v; { struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap = v; struct vnode *vp = ap->a_vp; register struct uio *uio = ap->a_uio; register struct iso_node *ip = VTOI(vp); register struct iso_mnt *imp; struct buf *bp; daddr_t lbn, rablock; off_t diff; int error = 0; long size, n, on; if (uio->uio_resid == 0) return (0); if (uio->uio_offset < 0) return (EINVAL); ip->i_flag |= IN_ACCESS; imp = ip->i_mnt; do { struct cluster_info *ci = &ip->i_ci; lbn = lblkno(imp, uio->uio_offset); on = blkoff(imp, uio->uio_offset); n = min((u_int)(imp->logical_block_size - on), uio->uio_resid); diff = (off_t)ip->i_size - uio->uio_offset; if (diff <= 0) return (0); if (diff < n) n = diff; size = blksize(imp, ip, lbn); rablock = lbn + 1; if (cd9660_doclusterread) { if (lblktosize(imp, rablock) <= ip->i_size) error = cluster_read(vp, &ip->i_ci, (off_t)ip->i_size, lbn, size, NOCRED, &bp); else error = bread(vp, lbn, size, NOCRED, &bp); } else { #define MAX_RA 32 if (ci->ci_lastr + 1 == lbn) { daddr_t rablks[MAX_RA]; int rasizes[MAX_RA]; int i; for (i = 0; i < MAX_RA && lblktosize(imp, (rablock + i)) < ip->i_size; i++) { rablks[i] = rablock + i; rasizes[i] = blksize(imp, ip, rablock + i); } error = breadn(vp, lbn, size, rablks, rasizes, i, NOCRED, &bp); } else error = bread(vp, lbn, size, NOCRED, &bp); } ci->ci_lastr = lbn; n = min(n, size - bp->b_resid); if (error) { brelse(bp); return (error); } error = uiomove(bp->b_data + on, (int)n, uio); if (n + on == imp->logical_block_size || uio->uio_offset == (off_t)ip->i_size) bp->b_flags |= B_AGE; brelse(bp); } while (error == 0 && uio->uio_resid > 0 && n != 0); return (error); } /* ARGSUSED */ int cd9660_ioctl(v) void *v; { struct vop_ioctl_args /* { struct vnode *a_vp; u_long a_command; caddr_t a_data; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap = v; daddr_t *block; switch (ap->a_command) { case FIBMAP: block = (daddr_t *)ap->a_data; return (VOP_BMAP(ap->a_vp, *block, NULL, block, 0)); default: return (ENOTTY); } } /* ARGSUSED */ int cd9660_poll(v) void *v; { struct vop_poll_args /* { struct vnode *a_vp; int a_events; struct proc *a_p; } */ *ap = v; /* * We should really check to see if I/O is possible. */ return (ap->a_events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)); } /* * Mmap a file * * NB Currently unsupported. */ /* ARGSUSED */ int cd9660_mmap(v) void *v; { return (EINVAL); } /* * Seek on a file * * Nothing to do, so just return. */ /* ARGSUSED */ int cd9660_seek(v) void *v; { return (0); } int iso_uiodir(idp,dp,off) struct isoreaddir *idp; struct dirent *dp; off_t off; { int error; dp->d_name[dp->d_namlen] = 0; dp->d_reclen = DIRENT_SIZE(dp); if (idp->uio->uio_resid < dp->d_reclen) { idp->eofflag = 0; return (-1); } if (idp->cookies) { if (idp->ncookies <= 0) { idp->eofflag = 0; return (-1); } *idp->cookies++ = off; --idp->ncookies; } if ((error = uiomove((caddr_t)dp, dp->d_reclen, idp->uio)) != 0) return (error); idp->uio_off = off; return (0); } int iso_shipdir(idp) struct isoreaddir *idp; { struct dirent *dp; int cl, sl, assoc; int error; char *cname, *sname; cl = idp->current.d_namlen; cname = idp->current.d_name; if ((assoc = cl > 1 && *cname == ASSOCCHAR)) { cl--; cname++; } dp = &idp->saveent; sname = dp->d_name; if (!(sl = dp->d_namlen)) { dp = &idp->assocent; sname = dp->d_name + 1; sl = dp->d_namlen - 1; } if (sl > 0) { if (sl != cl || bcmp(sname,cname,sl)) { if (idp->assocent.d_namlen) { error = iso_uiodir(idp, &idp->assocent, idp->assocoff); if (error) return (error); idp->assocent.d_namlen = 0; } if (idp->saveent.d_namlen) { error = iso_uiodir(idp, &idp->saveent, idp->saveoff); if (error) return (error); idp->saveent.d_namlen = 0; } } } idp->current.d_reclen = DIRENT_SIZE(&idp->current); if (assoc) { idp->assocoff = idp->curroff; bcopy(&idp->current,&idp->assocent,idp->current.d_reclen); } else { idp->saveoff = idp->curroff; bcopy(&idp->current,&idp->saveent,idp->current.d_reclen); } return (0); } /* * Vnode op for readdir */ int cd9660_readdir(v) void *v; { struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; int *a_eofflag; u_long *a_cookies; int a_ncookies; } */ *ap = v; register struct uio *uio = ap->a_uio; struct isoreaddir *idp; struct vnode *vdp = ap->a_vp; struct iso_node *dp; struct iso_mnt *imp; struct buf *bp = NULL; struct iso_directory_record *ep; int entryoffsetinblock; doff_t endsearch; u_long bmask; int error = 0; int reclen; u_short namelen; int ncookies = 0; u_long *cookies = NULL; dp = VTOI(vdp); imp = dp->i_mnt; bmask = imp->im_bmask; MALLOC(idp, struct isoreaddir *, sizeof(*idp), M_TEMP, M_WAITOK); idp->saveent.d_namlen = idp->assocent.d_namlen = 0; /* * XXX * Is it worth trying to figure out the type? */ idp->saveent.d_type = idp->assocent.d_type = idp->current.d_type = DT_UNKNOWN; idp->uio = uio; if (ap->a_ncookies == NULL) { idp->cookies = NULL; } else { /* * Guess the number of cookies needed. */ ncookies = uio->uio_resid / 16; MALLOC(cookies, u_long *, ncookies * sizeof(u_long), M_TEMP, M_WAITOK); idp->cookies = cookies; idp->ncookies = ncookies; } idp->eofflag = 1; idp->curroff = uio->uio_offset; idp->uio_off = uio->uio_offset; if ((entryoffsetinblock = idp->curroff & bmask) && (error = cd9660_bufatoff(dp, (off_t)idp->curroff, NULL, &bp))) { FREE(idp, M_TEMP); return (error); } endsearch = dp->i_size; while (idp->curroff < endsearch) { /* * If offset is on a block boundary, * read the next directory block. * Release previous if it exists. */ if ((idp->curroff & bmask) == 0) { if (bp != NULL) brelse(bp); error = cd9660_bufatoff(dp, (off_t)idp->curroff, NULL, &bp); if (error) break; entryoffsetinblock = 0; } /* * Get pointer to next entry. */ ep = (struct iso_directory_record *) ((char *)bp->b_data + entryoffsetinblock); reclen = isonum_711(ep->length); if (reclen == 0) { /* skip to next block, if any */ idp->curroff = (idp->curroff & ~bmask) + imp->logical_block_size; continue; } if (reclen < ISO_DIRECTORY_RECORD_SIZE) { error = EINVAL; /* illegal entry, stop */ break; } if (entryoffsetinblock + reclen > imp->logical_block_size) { error = EINVAL; /* illegal directory, so stop looking */ break; } idp->current.d_namlen = isonum_711(ep->name_len); if (reclen < ISO_DIRECTORY_RECORD_SIZE + idp->current.d_namlen) { error = EINVAL; /* illegal entry, stop */ break; } if (isonum_711(ep->flags)&2) idp->current.d_fileno = isodirino(ep, imp); else idp->current.d_fileno = dbtob(bp->b_blkno) + entryoffsetinblock; idp->curroff += reclen; switch (imp->iso_ftype) { case ISO_FTYPE_RRIP: cd9660_rrip_getname(ep,idp->current.d_name, &namelen, &idp->current.d_fileno,imp); idp->current.d_namlen = (u_char)namelen; if (idp->current.d_namlen) error = iso_uiodir(idp,&idp->current,idp->curroff); break; default: /* ISO_FTYPE_DEFAULT || ISO_FTYPE_9660 */ strlcpy(idp->current.d_name,"..", sizeof idp->current.d_name); if (idp->current.d_namlen == 1 && ep->name[0] == 0) { idp->current.d_namlen = 1; error = iso_uiodir(idp,&idp->current,idp->curroff); } else if (idp->current.d_namlen == 1 && ep->name[0] == 1) { idp->current.d_namlen = 2; error = iso_uiodir(idp,&idp->current,idp->curroff); } else { isofntrans(ep->name,idp->current.d_namlen, idp->current.d_name, &namelen, imp->iso_ftype == ISO_FTYPE_9660, isonum_711(ep->flags) & 4, imp->joliet_level); idp->current.d_namlen = (u_char)namelen; if (imp->iso_ftype == ISO_FTYPE_DEFAULT) error = iso_shipdir(idp); else error = iso_uiodir(idp,&idp->current,idp->curroff); } } if (error) break; entryoffsetinblock += reclen; } if (!error && imp->iso_ftype == ISO_FTYPE_DEFAULT) { idp->current.d_namlen = 0; error = iso_shipdir(idp); } if (error < 0) error = 0; if (ap->a_ncookies != NULL) { if (error) free(cookies, M_TEMP); else { /* * Work out the number of cookies actually used. */ *ap->a_ncookies = ncookies - idp->ncookies; *ap->a_cookies = cookies; } } if (bp) brelse (bp); uio->uio_offset = idp->uio_off; *ap->a_eofflag = idp->eofflag; FREE(idp, M_TEMP); return (error); } /* * Return target name of a symbolic link * Shouldn't we get the parent vnode and read the data from there? * This could eventually result in deadlocks in cd9660_lookup. * But otherwise the block read here is in the block buffer two times. */ typedef struct iso_directory_record ISODIR; typedef struct iso_node ISONODE; typedef struct iso_mnt ISOMNT; int cd9660_readlink(v) void *v; { struct vop_readlink_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap = v; ISONODE *ip; ISODIR *dirp; ISOMNT *imp; struct buf *bp; struct uio *uio; u_short symlen; int error; char *symname; ip = VTOI(ap->a_vp); imp = ip->i_mnt; uio = ap->a_uio; if (imp->iso_ftype != ISO_FTYPE_RRIP) return (EINVAL); /* * Get parents directory record block that this inode included. */ error = bread(imp->im_devvp, (ip->i_number >> imp->im_bshift) << (imp->im_bshift - DEV_BSHIFT), imp->logical_block_size, NOCRED, &bp); if (error) { brelse(bp); return (EINVAL); } /* * Setup the directory pointer for this inode */ dirp = (ISODIR *)(bp->b_data + (ip->i_number & imp->im_bmask)); /* * Just make sure, we have a right one.... * 1: Check not cross boundary on block */ if ((ip->i_number & imp->im_bmask) + isonum_711(dirp->length) > imp->logical_block_size) { brelse(bp); return (EINVAL); } /* * Now get a buffer * Abuse a namei buffer for now. */ if (uio->uio_segflg == UIO_SYSSPACE && uio->uio_iov->iov_len >= MAXPATHLEN) symname = uio->uio_iov->iov_base; else symname = pool_get(&namei_pool, PR_WAITOK); /* * Ok, we just gathering a symbolic name in SL record. */ if (cd9660_rrip_getsymname(dirp, symname, &symlen, imp) == 0) { if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iov->iov_len < MAXPATHLEN) pool_put(&namei_pool, symname); brelse(bp); return (EINVAL); } /* * Don't forget before you leave from home ;-) */ brelse(bp); /* * return with the symbolic name to caller's. */ if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iov->iov_len < MAXPATHLEN) { error = uiomove(symname, symlen, uio); pool_put(&namei_pool, symname); return (error); } uio->uio_resid -= symlen; uio->uio_iov->iov_base += symlen; uio->uio_iov->iov_len -= symlen; return (0); } int cd9660_link(v) void *v; { struct vop_link_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap = v; VOP_ABORTOP(ap->a_dvp, ap->a_cnp); vput(ap->a_dvp); return (EROFS); } int cd9660_symlink(v) void *v; { struct vop_symlink_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; char *a_target; } */ *ap = v; VOP_ABORTOP(ap->a_dvp, ap->a_cnp); vput(ap->a_dvp); return (EROFS); } /* * Lock an inode. */ int cd9660_lock(v) void *v; { struct vop_lock_args /* { struct vnode *a_vp; } */ *ap = v; struct vnode *vp = ap->a_vp; return (lockmgr(&VTOI(vp)->i_lock, ap->a_flags, &vp->v_interlock, ap->a_p)); } /* * Unlock an inode. */ int cd9660_unlock(v) void *v; { struct vop_unlock_args /* { struct vnode *a_vp; } */ *ap = v; struct vnode *vp = ap->a_vp; return (lockmgr(&VTOI(vp)->i_lock, ap->a_flags | LK_RELEASE, &vp->v_interlock, ap->a_p)); } /* * Calculate the logical to physical mapping if not done already, * then call the device strategy routine. */ int cd9660_strategy(v) void *v; { struct vop_strategy_args /* { struct buf *a_bp; } */ *ap = v; struct buf *bp = ap->a_bp; struct vnode *vp = bp->b_vp; struct iso_node *ip; int error; int s; ip = VTOI(vp); if (vp->v_type == VBLK || vp->v_type == VCHR) panic("cd9660_strategy: spec"); if (bp->b_blkno == bp->b_lblkno) { error = VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL); if (error) { bp->b_error = error; bp->b_flags |= B_ERROR; s = splbio(); biodone(bp); splx(s); return (error); } if ((long)bp->b_blkno == -1) clrbuf(bp); } if ((long)bp->b_blkno == -1) { s = splbio(); biodone(bp); splx(s); return (0); } vp = ip->i_devvp; bp->b_dev = vp->v_rdev; VOCALL (vp->v_op, VOFFSET(vop_strategy), ap); return (0); } /* * Print out the contents of an inode. */ /*ARGSUSED*/ int cd9660_print(v) void *v; { printf("tag VT_ISOFS, isofs vnode\n"); return (0); } /* * Check for a locked inode. */ int cd9660_islocked(v) void *v; { struct vop_islocked_args /* { struct vnode *a_vp; } */ *ap = v; return (lockstatus(&VTOI(ap->a_vp)->i_lock)); } /* * Return POSIX pathconf information applicable to cd9660 filesystems. */ int cd9660_pathconf(v) void *v; { struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; register_t *a_retval; } */ *ap = v; switch (ap->a_name) { case _PC_LINK_MAX: *ap->a_retval = 1; return (0); case _PC_NAME_MAX: if (VTOI(ap->a_vp)->i_mnt->iso_ftype == ISO_FTYPE_RRIP) *ap->a_retval = NAME_MAX; else *ap->a_retval = 37; return (0); case _PC_PATH_MAX: *ap->a_retval = PATH_MAX; return (0); case _PC_PIPE_BUF: *ap->a_retval = PIPE_BUF; return (0); case _PC_CHOWN_RESTRICTED: *ap->a_retval = 1; return (0); case _PC_NO_TRUNC: *ap->a_retval = 1; return (0); default: return (EINVAL); } /* NOTREACHED */ } /* * Global vfs data structures for isofs */ #define cd9660_create eopnotsupp #define cd9660_mknod eopnotsupp #define cd9660_write eopnotsupp #ifdef NFSSERVER int lease_check(void *); #define cd9660_lease_check lease_check #else #define cd9660_lease_check nullop #endif #define cd9660_fsync nullop #define cd9660_remove eopnotsupp #define cd9660_rename eopnotsupp #define cd9660_mkdir eopnotsupp #define cd9660_rmdir eopnotsupp #define cd9660_advlock eopnotsupp #define cd9660_valloc eopnotsupp #define cd9660_vfree eopnotsupp #define cd9660_truncate eopnotsupp #define cd9660_update eopnotsupp #define cd9660_bwrite eopnotsupp #define cd9660_revoke vop_generic_revoke /* * Global vfs data structures for cd9660 */ int (**cd9660_vnodeop_p)(void *); struct vnodeopv_entry_desc cd9660_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, cd9660_lookup }, /* lookup */ { &vop_create_desc, cd9660_create }, /* create */ { &vop_mknod_desc, cd9660_mknod }, /* mknod */ { &vop_open_desc, cd9660_open }, /* open */ { &vop_close_desc, cd9660_close }, /* close */ { &vop_access_desc, cd9660_access }, /* access */ { &vop_getattr_desc, cd9660_getattr }, /* getattr */ { &vop_setattr_desc, cd9660_setattr }, /* setattr */ { &vop_read_desc, cd9660_read }, /* read */ { &vop_write_desc, cd9660_write }, /* write */ { &vop_lease_desc, cd9660_lease_check },/* lease */ { &vop_ioctl_desc, cd9660_ioctl }, /* ioctl */ { &vop_poll_desc, cd9660_poll }, /* poll */ { &vop_revoke_desc, cd9660_revoke }, /* revoke */ { &vop_fsync_desc, cd9660_fsync }, /* fsync */ { &vop_remove_desc, cd9660_remove }, /* remove */ { &vop_link_desc, cd9660_link }, /* link */ { &vop_rename_desc, cd9660_rename }, /* rename */ { &vop_mkdir_desc, cd9660_mkdir }, /* mkdir */ { &vop_rmdir_desc, cd9660_rmdir }, /* rmdir */ { &vop_symlink_desc, cd9660_symlink }, /* symlink */ { &vop_readdir_desc, cd9660_readdir }, /* readdir */ { &vop_readlink_desc, cd9660_readlink },/* readlink */ { &vop_abortop_desc, vop_generic_abortop }, /* abortop */ { &vop_inactive_desc, cd9660_inactive },/* inactive */ { &vop_reclaim_desc, cd9660_reclaim }, /* reclaim */ { &vop_lock_desc, cd9660_lock }, /* lock */ { &vop_unlock_desc, cd9660_unlock }, /* unlock */ { &vop_bmap_desc, cd9660_bmap }, /* bmap */ { &vop_strategy_desc, cd9660_strategy },/* strategy */ { &vop_print_desc, cd9660_print }, /* print */ { &vop_islocked_desc, cd9660_islocked },/* islocked */ { &vop_pathconf_desc, cd9660_pathconf },/* pathconf */ { &vop_advlock_desc, cd9660_advlock }, /* advlock */ { &vop_bwrite_desc, vop_generic_bwrite }, { NULL, NULL } }; struct vnodeopv_desc cd9660_vnodeop_opv_desc = { &cd9660_vnodeop_p, cd9660_vnodeop_entries }; /* * Special device vnode ops */ int (**cd9660_specop_p)(void *); struct vnodeopv_entry_desc cd9660_specop_entries[] = { { &vop_default_desc, spec_vnoperate }, { &vop_access_desc, cd9660_access }, /* access */ { &vop_getattr_desc, cd9660_getattr }, /* getattr */ { &vop_setattr_desc, cd9660_setattr }, /* setattr */ { &vop_inactive_desc, cd9660_inactive },/* inactive */ { &vop_reclaim_desc, cd9660_reclaim }, /* reclaim */ { &vop_lock_desc, cd9660_lock }, /* lock */ { &vop_unlock_desc, cd9660_unlock }, /* unlock */ { &vop_print_desc, cd9660_print }, /* print */ { &vop_islocked_desc, cd9660_islocked },/* islocked */ { NULL, NULL } }; struct vnodeopv_desc cd9660_specop_opv_desc = { &cd9660_specop_p, cd9660_specop_entries }; #ifdef FIFO int (**cd9660_fifoop_p)(void *); struct vnodeopv_entry_desc cd9660_fifoop_entries[] = { { &vop_default_desc, fifo_vnoperate }, { &vop_access_desc, cd9660_access }, /* access */ { &vop_getattr_desc, cd9660_getattr }, /* getattr */ { &vop_setattr_desc, cd9660_setattr }, /* setattr */ { &vop_inactive_desc, cd9660_inactive },/* inactive */ { &vop_reclaim_desc, cd9660_reclaim }, /* reclaim */ { &vop_lock_desc, cd9660_lock }, /* lock */ { &vop_unlock_desc, cd9660_unlock }, /* unlock */ { &vop_print_desc, cd9660_print }, /* print */ { &vop_islocked_desc, cd9660_islocked },/* islocked */ { &vop_bwrite_desc, vop_generic_bwrite }, { NULL, NULL } }; struct vnodeopv_desc cd9660_fifoop_opv_desc = { &cd9660_fifoop_p, cd9660_fifoop_entries }; #endif /* FIFO */ makefs/src/sys/isofs/cd9660/iso.h010064400000000000000000000244571314214545100136750ustar00/** $MirOS: src/sys/isofs/cd9660/iso.h,v 1.4 2017/08/07 20:18:22 tg Exp $ */ /* $OpenBSD: iso.h,v 1.13 2003/06/02 23:28:05 millert Exp $ */ /* $NetBSD: iso.h,v 1.20 1997/07/07 22:45:34 cgd Exp $ */ /*- * Copyright © 2013 * Thorsten “mirabilos” Glaser * Copyright (c) 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. * * @(#)iso.h 8.4 (Berkeley) 12/5/94 */ /* * Definitions describing ISO9660 filesystem structure, as well as * the functions necessary to access fields of ISO9660 filesystem * structures. */ #define ISODCL(from, to) (to - from + 1) struct iso_volume_descriptor { char type[ISODCL(1,1)]; /* 711 */ char id[ISODCL(2,6)]; char version[ISODCL(7,7)]; char data[ISODCL(8,2048)]; }; /* volume descriptor types */ #define ISO_VD_PRIMARY 1 #define ISO_VD_SUPPLEMENTARY 2 #define ISO_VD_END 255 #define ISO_STANDARD_ID "CD001" #define ISO_ECMA_ID "CDW01" struct iso_primary_descriptor { char type [ISODCL ( 1, 1)]; /* 711 */ char id [ISODCL ( 2, 6)]; char version [ISODCL ( 7, 7)]; /* 711 */ char unused1 [ISODCL ( 8, 8)]; char system_id [ISODCL ( 9, 40)]; /* achars */ char volume_id [ISODCL ( 41, 72)]; /* dchars */ char unused2 [ISODCL ( 73, 80)]; char volume_space_size [ISODCL ( 81, 88)]; /* 733 */ char unused3 [ISODCL ( 89, 120)]; char volume_set_size [ISODCL (121, 124)]; /* 723 */ char volume_sequence_number [ISODCL (125, 128)]; /* 723 */ char logical_block_size [ISODCL (129, 132)]; /* 723 */ char path_table_size [ISODCL (133, 140)]; /* 733 */ char type_l_path_table [ISODCL (141, 144)]; /* 731 */ char opt_type_l_path_table [ISODCL (145, 148)]; /* 731 */ char type_m_path_table [ISODCL (149, 152)]; /* 732 */ char opt_type_m_path_table [ISODCL (153, 156)]; /* 732 */ char root_directory_record [ISODCL (157, 190)]; /* 9.1 */ char volume_set_id [ISODCL (191, 318)]; /* dchars */ char publisher_id [ISODCL (319, 446)]; /* achars */ char preparer_id [ISODCL (447, 574)]; /* achars */ char application_id [ISODCL (575, 702)]; /* achars */ char copyright_file_id [ISODCL (703, 739)]; /* 7.5 dchars */ char abstract_file_id [ISODCL (740, 776)]; /* 7.5 dchars */ char bibliographic_file_id [ISODCL (777, 813)]; /* 7.5 dchars */ char creation_date [ISODCL (814, 830)]; /* 8.4.26.1 */ char modification_date [ISODCL (831, 847)]; /* 8.4.26.1 */ char expiration_date [ISODCL (848, 864)]; /* 8.4.26.1 */ char effective_date [ISODCL (865, 881)]; /* 8.4.26.1 */ char file_structure_version [ISODCL (882, 882)]; /* 711 */ char unused4 [ISODCL (883, 883)]; char application_data [ISODCL (884, 1395)]; char unused5 [ISODCL (1396, 2048)]; }; #define ISO_DEFAULT_BLOCK_SHIFT 11 #define ISO_DEFAULT_BLOCK_SIZE (1< #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern u_long nextgennumber; static daddr_t ffs_alloccg(struct inode *, int, daddr_t, int); static daddr_t ffs_alloccgblk(struct inode *, struct buf *, daddr_t); static daddr_t ffs_clusteralloc(struct inode *, int, daddr_t, int); static ino_t ffs_dirpref(struct inode *); static daddr_t ffs_fragextend(struct inode *, int, long, int, int); static void ffs_fserr(struct fs *, u_int, char *); static u_long ffs_hashalloc(struct inode *, int, long, int, daddr_t (*)(struct inode *, int, daddr_t, int)); static daddr_t ffs_nodealloccg(struct inode *, int, daddr_t, int); static daddr_t ffs_mapsearch(struct fs *, struct cg *, daddr_t, int); #ifdef DIAGNOSTIC static int ffs_checkblk(struct inode *, daddr_t, long); #endif /* * Allocate a block in the filesystem. * * The size of the requested block is given, which must be some * multiple of fs_fsize and <= fs_bsize. * A preference may be optionally specified. If a preference is given * the following hierarchy is used to allocate a block: * 1) allocate the requested block. * 2) allocate a rotationally optimal block in the same cylinder. * 3) allocate a block in the same cylinder group. * 4) quadratically rehash into other cylinder groups, until an * available block is located. * If no block preference is given the following hierarchy is used * to allocate a block: * 1) allocate a block in the cylinder group that contains the * inode for the file. * 2) quadratically rehash into other cylinder groups, until an * available block is located. */ int ffs_alloc(ip, lbn, bpref, size, cred, bnp) register struct inode *ip; daddr_t lbn, bpref; int size; struct ucred *cred; daddr_t *bnp; { register struct fs *fs; daddr_t bno; int cg; int error; *bnp = 0; fs = ip->i_fs; #ifdef DIAGNOSTIC if ((u_int)size > fs->fs_bsize || fragoff(fs, size) != 0) { printf("dev = 0x%x, bsize = %d, size = %d, fs = %s\n", ip->i_dev, fs->fs_bsize, size, fs->fs_fsmnt); panic("ffs_alloc: bad size"); } if (cred == NOCRED) panic("ffs_alloc: missing credential"); #endif /* DIAGNOSTIC */ if (size == fs->fs_bsize && fs->fs_cstotal.cs_nbfree == 0) goto nospace; if (cred->cr_uid != 0 && freespace(fs, fs->fs_minfree) <= 0) goto nospace; if ((error = ufs_quota_alloc_blocks(ip, btodb(size), cred)) != 0) return (error); if (bpref >= fs->fs_size) bpref = 0; if (bpref == 0) cg = ino_to_cg(fs, ip->i_number); else cg = dtog(fs, bpref); bno = (daddr_t)ffs_hashalloc(ip, cg, (long)bpref, size, ffs_alloccg); if (bno > 0) { ip->i_ffs_blocks += btodb(size); ip->i_flag |= IN_CHANGE | IN_UPDATE; *bnp = bno; return (0); } /* * Restore user's disk quota because allocation failed. */ (void) ufs_quota_free_blocks(ip, btodb(size), cred); nospace: ffs_fserr(fs, cred->cr_uid, "filesystem full"); uprintf("\n%s: write failed, filesystem is full\n", fs->fs_fsmnt); return (ENOSPC); } /* * Reallocate a fragment to a bigger size * * The number and size of the old block is given, and a preference * and new size is also specified. The allocator attempts to extend * the original block. Failing that, the regular block allocator is * invoked to get an appropriate block. */ int ffs_realloccg(ip, lbprev, bpref, osize, nsize, cred, bpp, blknop) register struct inode *ip; daddr_t lbprev; daddr_t bpref; int osize, nsize; struct ucred *cred; struct buf **bpp; daddr_t *blknop; { register struct fs *fs; struct buf *bp = NULL; daddr_t quota_updated = 0; int cg, request, error; daddr_t bprev, bno; if (bpp != NULL) *bpp = NULL; fs = ip->i_fs; #ifdef DIAGNOSTIC if ((u_int)osize > fs->fs_bsize || fragoff(fs, osize) != 0 || (u_int)nsize > fs->fs_bsize || fragoff(fs, nsize) != 0) { printf( "dev = 0x%x, bsize = %d, osize = %d, nsize = %d, fs = %s\n", ip->i_dev, fs->fs_bsize, osize, nsize, fs->fs_fsmnt); panic("ffs_realloccg: bad size"); } if (cred == NOCRED) panic("ffs_realloccg: missing credential"); #endif /* DIAGNOSTIC */ if (cred->cr_uid != 0 && freespace(fs, fs->fs_minfree) <= 0) goto nospace; if ((bprev = ip->i_ffs_db[lbprev]) == 0) { printf("dev = 0x%x, bsize = %d, bprev = %d, fs = %s\n", ip->i_dev, fs->fs_bsize, bprev, fs->fs_fsmnt); panic("ffs_realloccg: bad bprev"); } /* * Allocate the extra space in the buffer. */ if (bpp != NULL && (error = bread(ITOV(ip), lbprev, osize, NOCRED, &bp)) != 0) goto error; if ((error = ufs_quota_alloc_blocks(ip, btodb(nsize - osize), cred)) != 0) goto error; quota_updated = btodb(nsize - osize); /* * Check for extension in the existing location. */ cg = dtog(fs, bprev); if ((bno = ffs_fragextend(ip, cg, (long)bprev, osize, nsize)) != 0) { ip->i_ffs_blocks += btodb(nsize - osize); ip->i_flag |= IN_CHANGE | IN_UPDATE; if (bpp != NULL) { if (bp->b_blkno != fsbtodb(fs, bno)) panic("ffs_realloccg: bad blockno"); allocbuf(bp, nsize); bp->b_flags |= B_DONE; bzero((char *)bp->b_data + osize, (u_int)nsize - osize); *bpp = bp; } if (blknop != NULL) { *blknop = bno; } return (0); } /* * Allocate a new disk location. */ if (bpref >= fs->fs_size) bpref = 0; switch ((int)fs->fs_optim) { case FS_OPTSPACE: /* * Allocate an exact sized fragment. Although this makes * best use of space, we will waste time relocating it if * the file continues to grow. If the fragmentation is * less than half of the minimum free reserve, we choose * to begin optimizing for time. */ request = nsize; if (fs->fs_minfree < 5 || fs->fs_cstotal.cs_nffree > fs->fs_dsize * fs->fs_minfree / (2 * 100)) break; log(LOG_NOTICE, "%s: optimization changed from SPACE to TIME\n", fs->fs_fsmnt); fs->fs_optim = FS_OPTTIME; break; case FS_OPTTIME: /* * At this point we have discovered a file that is trying to * grow a small fragment to a larger fragment. To save time, * we allocate a full sized block, then free the unused portion. * If the file continues to grow, the `ffs_fragextend' call * above will be able to grow it in place without further * copying. If aberrant programs cause disk fragmentation to * grow within 2% of the free reserve, we choose to begin * optimizing for space. */ request = fs->fs_bsize; if (fs->fs_cstotal.cs_nffree < fs->fs_dsize * (fs->fs_minfree - 2) / 100) break; log(LOG_NOTICE, "%s: optimization changed from TIME to SPACE\n", fs->fs_fsmnt); fs->fs_optim = FS_OPTSPACE; break; default: printf("dev = 0x%x, optim = %d, fs = %s\n", ip->i_dev, fs->fs_optim, fs->fs_fsmnt); panic("ffs_realloccg: bad optim"); /* NOTREACHED */ } bno = (daddr_t)ffs_hashalloc(ip, cg, (long)bpref, request, ffs_alloccg); if (bno <= 0) goto nospace; (void) uvm_vnp_uncache(ITOV(ip)); if (!DOINGSOFTDEP(ITOV(ip))) ffs_blkfree(ip, bprev, (long)osize); if (nsize < request) ffs_blkfree(ip, bno + numfrags(fs, nsize), (long)(request - nsize)); ip->i_ffs_blocks += btodb(nsize - osize); ip->i_flag |= IN_CHANGE | IN_UPDATE; if (bpp != NULL) { bp->b_blkno = fsbtodb(fs, bno); allocbuf(bp, nsize); bp->b_flags |= B_DONE; bzero((char *)bp->b_data + osize, (u_int)nsize - osize); *bpp = bp; } if (blknop != NULL) { *blknop = bno; } return (0); nospace: /* * no space available */ ffs_fserr(fs, cred->cr_uid, "filesystem full"); uprintf("\n%s: write failed, filesystem is full\n", fs->fs_fsmnt); error = ENOSPC; error: if (bp != NULL) { brelse(bp); bp = NULL; } /* * Restore user's disk quota because allocation failed. */ if (quota_updated != 0) (void)ufs_quota_free_blocks(ip, quota_updated, cred); return error; } /* * Reallocate a sequence of blocks into a contiguous sequence of blocks. * * The vnode and an array of buffer pointers for a range of sequential * logical blocks to be made contiguous are given. The allocator attempts * to find a range of sequential blocks starting as close as possible to * an fs_rotdelay offset from the end of the allocation for the logical * block immediately preceding the current range. If successful, the * physical block numbers in the buffer pointers and in the inode are * changed to reflect the new allocation. If unsuccessful, the allocation * is left unchanged. The success in doing the reallocation is returned. * Note that the error return is not reflected back to the user. Rather * the previous block allocation will be used. */ int doasyncfree = 1; int doreallocblks = 1; int prtrealloc = 0; int ffs_reallocblks(v) void *v; { struct vop_reallocblks_args /* { struct vnode *a_vp; struct cluster_save *a_buflist; } */ *ap = v; struct fs *fs; struct inode *ip; struct vnode *vp; struct buf *sbp, *ebp; daddr_t *bap, *sbap, *ebap = NULL; struct cluster_save *buflist; daddr_t start_lbn, end_lbn, soff, newblk, blkno; struct indir start_ap[NIADDR + 1], end_ap[NIADDR + 1], *idp; int i, len, start_lvl, end_lvl, pref, ssize; if (doreallocblks == 0) return (ENOSPC); vp = ap->a_vp; ip = VTOI(vp); fs = ip->i_fs; if (fs->fs_contigsumsize <= 0) return (ENOSPC); buflist = ap->a_buflist; len = buflist->bs_nchildren; start_lbn = buflist->bs_children[0]->b_lblkno; end_lbn = start_lbn + len - 1; #ifdef DIAGNOSTIC for (i = 0; i < len; i++) if (!ffs_checkblk(ip, dbtofsb(fs, buflist->bs_children[i]->b_blkno), fs->fs_bsize)) panic("ffs_reallocblks: unallocated block 1"); for (i = 1; i < len; i++) if (buflist->bs_children[i]->b_lblkno != start_lbn + i) panic("ffs_reallocblks: non-logical cluster"); blkno = buflist->bs_children[0]->b_blkno; ssize = fsbtodb(fs, fs->fs_frag); for (i = 1; i < len - 1; i++) if (buflist->bs_children[i]->b_blkno != blkno + (i * ssize)) panic("ffs_reallocblks: non-physical cluster %d", i); #endif /* * If the latest allocation is in a new cylinder group, assume that * the filesystem has decided to move and do not force it back to * the previous cylinder group. */ if (dtog(fs, dbtofsb(fs, buflist->bs_children[0]->b_blkno)) != dtog(fs, dbtofsb(fs, buflist->bs_children[len - 1]->b_blkno))) return (ENOSPC); if (ufs_getlbns(vp, start_lbn, start_ap, &start_lvl) || ufs_getlbns(vp, end_lbn, end_ap, &end_lvl)) return (ENOSPC); /* * Get the starting offset and block map for the first block. */ if (start_lvl == 0) { sbap = &ip->i_ffs_db[0]; soff = start_lbn; } else { idp = &start_ap[start_lvl - 1]; if (bread(vp, idp->in_lbn, (int)fs->fs_bsize, NOCRED, &sbp)) { brelse(sbp); return (ENOSPC); } sbap = (daddr_t *)sbp->b_data; soff = idp->in_off; } /* * Find the preferred location for the cluster. */ pref = ffs_blkpref(ip, start_lbn, soff, sbap); /* * If the block range spans two block maps, get the second map. */ if (end_lvl == 0 || (idp = &end_ap[end_lvl - 1])->in_off + 1 >= len) { ssize = len; } else { #ifdef DIAGNOSTIC if (start_lvl > 1 && start_ap[start_lvl-1].in_lbn == idp->in_lbn) panic("ffs_reallocblk: start == end"); #endif ssize = len - (idp->in_off + 1); if (bread(vp, idp->in_lbn, (int)fs->fs_bsize, NOCRED, &ebp)) goto fail; ebap = (daddr_t *)ebp->b_data; } /* * Search the block map looking for an allocation of the desired size. */ if ((newblk = (daddr_t)ffs_hashalloc(ip, dtog(fs, pref), (long)pref, len, ffs_clusteralloc)) == 0) goto fail; /* * We have found a new contiguous block. * * First we have to replace the old block pointers with the new * block pointers in the inode and indirect blocks associated * with the file. */ #ifdef DEBUG if (prtrealloc) printf("realloc: ino %d, lbns %d-%d\n\told:", ip->i_number, start_lbn, end_lbn); #endif blkno = newblk; for (bap = &sbap[soff], i = 0; i < len; i++, blkno += fs->fs_frag) { if (i == ssize) { bap = ebap; soff = -i; } #ifdef DIAGNOSTIC if (!ffs_checkblk(ip, dbtofsb(fs, buflist->bs_children[i]->b_blkno), fs->fs_bsize)) panic("ffs_reallocblks: unallocated block 2"); if (dbtofsb(fs, buflist->bs_children[i]->b_blkno) != *bap) panic("ffs_reallocblks: alloc mismatch"); #endif #ifdef DEBUG if (prtrealloc) printf(" %d,", *bap); #endif if (DOINGSOFTDEP(vp)) { if (sbap == &ip->i_ffs_db[0] && i < ssize) softdep_setup_allocdirect(ip, start_lbn + i, blkno, *bap, fs->fs_bsize, fs->fs_bsize, buflist->bs_children[i]); else softdep_setup_allocindir_page(ip, start_lbn + i, i < ssize ? sbp : ebp, soff + i, blkno, *bap, buflist->bs_children[i]); } *bap++ = blkno; } /* * Next we must write out the modified inode and indirect blocks. * For strict correctness, the writes should be synchronous since * the old block values may have been written to disk. In practise * they are almost never written, but if we are concerned about * strict correctness, the `doasyncfree' flag should be set to zero. * * The test on `doasyncfree' should be changed to test a flag * that shows whether the associated buffers and inodes have * been written. The flag should be set when the cluster is * started and cleared whenever the buffer or inode is flushed. * We can then check below to see if it is set, and do the * synchronous write only when it has been cleared. */ if (sbap != &ip->i_ffs_db[0]) { if (doasyncfree) bdwrite(sbp); else bwrite(sbp); } else { ip->i_flag |= IN_CHANGE | IN_UPDATE; if (!doasyncfree) { UFS_UPDATE(ip, MNT_WAIT); } } if (ssize < len) { if (doasyncfree) bdwrite(ebp); else bwrite(ebp); } /* * Last, free the old blocks and assign the new blocks to the buffers. */ #ifdef DEBUG if (prtrealloc) printf("\n\tnew:"); #endif for (blkno = newblk, i = 0; i < len; i++, blkno += fs->fs_frag) { if (!DOINGSOFTDEP(vp)) ffs_blkfree(ip, dbtofsb(fs, buflist->bs_children[i]->b_blkno), fs->fs_bsize); buflist->bs_children[i]->b_blkno = fsbtodb(fs, blkno); #ifdef DIAGNOSTIC if (!ffs_checkblk(ip, dbtofsb(fs, buflist->bs_children[i]->b_blkno), fs->fs_bsize)) panic("ffs_reallocblks: unallocated block 3"); if (prtrealloc) printf(" %d,", blkno); #endif } #ifdef DEBUG if (prtrealloc) { prtrealloc--; printf("\n"); } #endif return (0); fail: if (ssize < len) brelse(ebp); if (sbap != &ip->i_ffs_db[0]) brelse(sbp); return (ENOSPC); } /* * Allocate an inode in the filesystem. * * If allocating a directory, use ffs_dirpref to select the inode. * If allocating in a directory, the following hierarchy is followed: * 1) allocate the preferred inode. * 2) allocate an inode in the same cylinder group. * 3) quadratically rehash into other cylinder groups, until an * available inode is located. * If no inode preference is given the following hierarchy is used * to allocate an inode: * 1) allocate an inode in cylinder group 0. * 2) quadratically rehash into other cylinder groups, until an * available inode is located. */ int ffs_inode_alloc(struct inode *pip, mode_t mode, struct ucred *cred, struct vnode **vpp) { struct vnode *pvp = ITOV(pip); struct fs *fs; struct inode *ip; ino_t ino, ipref; int cg, error; *vpp = NULL; fs = pip->i_fs; if (fs->fs_cstotal.cs_nifree == 0) goto noinodes; if ((mode & IFMT) == IFDIR) ipref = ffs_dirpref(pip); else ipref = pip->i_number; if (ipref >= fs->fs_ncg * fs->fs_ipg) ipref = 0; cg = ino_to_cg(fs, ipref); /* * Track number of dirs created one after another * in a same cg without intervening by files. */ if ((mode & IFMT) == IFDIR) { if (fs->fs_contigdirs[cg] < 255) fs->fs_contigdirs[cg]++; } else { if (fs->fs_contigdirs[cg] > 0) fs->fs_contigdirs[cg]--; } ino = (ino_t)ffs_hashalloc(pip, cg, (long)ipref, mode, ffs_nodealloccg); if (ino == 0) goto noinodes; error = VFS_VGET(pvp->v_mount, ino, vpp); if (error) { ffs_inode_free(pip, ino, mode); return (error); } ip = VTOI(*vpp); if (ip->i_ffs_mode) { printf("mode = 0%o, inum = %d, fs = %s\n", ip->i_ffs_mode, ip->i_number, fs->fs_fsmnt); panic("ffs_valloc: dup alloc"); } if (ip->i_ffs_blocks) { /* XXX */ printf("free inode %s/%d had %d blocks\n", fs->fs_fsmnt, ino, ip->i_ffs_blocks); ip->i_ffs_blocks = 0; } ip->i_ffs_flags = 0; /* * Set up a new generation number for this inode. * XXX - just increment for now, this is wrong! (millert) * Need a way to preserve randomization. */ if (ip->i_ffs_gen == 0 || ++(ip->i_ffs_gen) == 0) ip->i_ffs_gen = arc4random() & INT_MAX; if (ip->i_ffs_gen == 0 || ip->i_ffs_gen == -1) ip->i_ffs_gen = 1; /* shouldn't happen */ return (0); noinodes: ffs_fserr(fs, cred->cr_uid, "out of inodes"); uprintf("\n%s: create/symlink failed, no inodes free\n", fs->fs_fsmnt); return (ENOSPC); } /* * Find a cylinder group to place a directory. * * The policy implemented by this algorithm is to allocate a * directory inode in the same cylinder group as its parent * directory, but also to reserve space for its files inodes * and data. Restrict the number of directories which may be * allocated one after another in the same cylinder group * without intervening allocation of files. * * If we allocate a first level directory then force allocation * in another cylinder group. */ static ino_t ffs_dirpref(pip) struct inode *pip; { register struct fs *fs; int cg, prefcg, dirsize, cgsize; int avgifree, avgbfree, avgndir, curdirsize; int minifree, minbfree, maxndir; int mincg, minndir; int maxcontigdirs; fs = pip->i_fs; avgifree = fs->fs_cstotal.cs_nifree / fs->fs_ncg; avgbfree = fs->fs_cstotal.cs_nbfree / fs->fs_ncg; avgndir = fs->fs_cstotal.cs_ndir / fs->fs_ncg; #if 1 /* * Force allocation in another cg if creating a first level dir. */ if (ITOV(pip)->v_flag & VROOT) { prefcg = (arc4random() & INT_MAX) % fs->fs_ncg; mincg = prefcg; minndir = fs->fs_ipg; for (cg = prefcg; cg < fs->fs_ncg; cg++) if (fs->fs_cs(fs, cg).cs_ndir < minndir && fs->fs_cs(fs, cg).cs_nifree >= avgifree && fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) { mincg = cg; minndir = fs->fs_cs(fs, cg).cs_ndir; } for (cg = 0; cg < prefcg; cg++) if (fs->fs_cs(fs, cg).cs_ndir < minndir && fs->fs_cs(fs, cg).cs_nifree >= avgifree && fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) { mincg = cg; minndir = fs->fs_cs(fs, cg).cs_ndir; } cg = mincg; goto end; } else prefcg = ino_to_cg(fs, pip->i_number); #else prefcg = ino_to_cg(fs, pip->i_number); #endif /* * Count various limits which used for * optimal allocation of a directory inode. */ #if 1 maxndir = min(avgndir + fs->fs_ipg / 16, fs->fs_ipg); minifree = avgifree - fs->fs_ipg / 4; if (minifree < 0) minifree = 0; minbfree = avgbfree - fs->fs_fpg / fs->fs_frag / 4; if (minbfree < 0) minbfree = 0; #else maxndir = avgndir + (fs->fs_ipg - avgndir) / 16; minifree = avgifree * 3 / 4; minbfree = avgbfree * 3 / 4; #endif cgsize = fs->fs_fsize * fs->fs_fpg; dirsize = fs->fs_avgfilesize * fs->fs_avgfpdir; curdirsize = avgndir ? (cgsize - avgbfree * fs->fs_bsize) / avgndir : 0; if (dirsize < curdirsize) dirsize = curdirsize; maxcontigdirs = min(cgsize / dirsize, 255); if (fs->fs_avgfpdir > 0) maxcontigdirs = min(maxcontigdirs, fs->fs_ipg / fs->fs_avgfpdir); if (maxcontigdirs == 0) maxcontigdirs = 1; /* * Limit number of dirs in one cg and reserve space for * regular files, but only if we have no deficit in * inodes or space. */ for (cg = prefcg; cg < fs->fs_ncg; cg++) if (fs->fs_cs(fs, cg).cs_ndir < maxndir && fs->fs_cs(fs, cg).cs_nifree >= minifree && fs->fs_cs(fs, cg).cs_nbfree >= minbfree) { if (fs->fs_contigdirs[cg] < maxcontigdirs) goto end; } for (cg = 0; cg < prefcg; cg++) if (fs->fs_cs(fs, cg).cs_ndir < maxndir && fs->fs_cs(fs, cg).cs_nifree >= minifree && fs->fs_cs(fs, cg).cs_nbfree >= minbfree) { if (fs->fs_contigdirs[cg] < maxcontigdirs) goto end; } /* * This is a backstop when we have deficit in space. */ for (cg = prefcg; cg < fs->fs_ncg; cg++) if (fs->fs_cs(fs, cg).cs_nifree >= avgifree) goto end; for (cg = 0; cg < prefcg; cg++) if (fs->fs_cs(fs, cg).cs_nifree >= avgifree) goto end; end: return ((ino_t)(fs->fs_ipg * cg)); } /* * Select the desired position for the next block in a file. The file is * logically divided into sections. The first section is composed of the * direct blocks. Each additional section contains fs_maxbpg blocks. * * If no blocks have been allocated in the first section, the policy is to * request a block in the same cylinder group as the inode that describes * the file. If no blocks have been allocated in any other section, the * policy is to place the section in a cylinder group with a greater than * average number of free blocks. An appropriate cylinder group is found * by using a rotor that sweeps the cylinder groups. When a new group of * blocks is needed, the sweep begins in the cylinder group following the * cylinder group from which the previous allocation was made. The sweep * continues until a cylinder group with greater than the average number * of free blocks is found. If the allocation is for the first block in an * indirect block, the information on the previous allocation is unavailable; * here a best guess is made based upon the logical block number being * allocated. * * If a section is already partially allocated, the policy is to * contiguously allocate fs_maxcontig blocks. The end of one of these * contiguous blocks and the beginning of the next is physically separated * so that the disk head will be in transit between them for at least * fs_rotdelay milliseconds. This is to allow time for the processor to * schedule another I/O transfer. */ daddr_t ffs_blkpref(ip, lbn, indx, bap) struct inode *ip; daddr_t lbn; int indx; daddr_t *bap; { register struct fs *fs; register int cg; int avgbfree, startcg; daddr_t nextblk; fs = ip->i_fs; if (indx % fs->fs_maxbpg == 0 || bap[indx - 1] == 0) { if (lbn < NDADDR + NINDIR(fs)) { cg = ino_to_cg(fs, ip->i_number); return (fs->fs_fpg * cg + fs->fs_frag); } /* * Find a cylinder with greater than average number of * unused data blocks. */ if (indx == 0 || bap[indx - 1] == 0) startcg = ino_to_cg(fs, ip->i_number) + lbn / fs->fs_maxbpg; else startcg = dtog(fs, bap[indx - 1]) + 1; startcg %= fs->fs_ncg; avgbfree = fs->fs_cstotal.cs_nbfree / fs->fs_ncg; for (cg = startcg; cg < fs->fs_ncg; cg++) if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) { fs->fs_cgrotor = cg; return (fs->fs_fpg * cg + fs->fs_frag); } for (cg = 0; cg <= startcg; cg++) if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) { fs->fs_cgrotor = cg; return (fs->fs_fpg * cg + fs->fs_frag); } return (0); } /* * One or more previous blocks have been laid out. If less * than fs_maxcontig previous blocks are contiguous, the * next block is requested contiguously, otherwise it is * requested rotationally delayed by fs_rotdelay milliseconds. */ nextblk = bap[indx - 1] + fs->fs_frag; if (indx < fs->fs_maxcontig || bap[indx - fs->fs_maxcontig] + blkstofrags(fs, fs->fs_maxcontig) != nextblk) return (nextblk); if (fs->fs_rotdelay != 0) /* * Here we convert ms of delay to frags as: * (frags) = (ms) * (rev/sec) * (sect/rev) / * ((sect/frag) * (ms/sec)) * then round up to the next block. */ nextblk += roundup(fs->fs_rotdelay * fs->fs_rps * fs->fs_nsect / (NSPF(fs) * 1000), fs->fs_frag); return (nextblk); } /* * Implement the cylinder overflow algorithm. * * The policy implemented by this algorithm is: * 1) allocate the block in its requested cylinder group. * 2) quadratically rehash on the cylinder group number. * 3) brute force search for a free block. */ /*VARARGS5*/ static u_long ffs_hashalloc(ip, cg, pref, size, allocator) struct inode *ip; int cg; long pref; int size; /* size for data blocks, mode for inodes */ daddr_t (*allocator)(struct inode *, int, daddr_t, int); { register struct fs *fs; long result; int i, icg = cg; fs = ip->i_fs; /* * 1: preferred cylinder group */ result = (*allocator)(ip, cg, pref, size); if (result) return (result); /* * 2: quadratic rehash */ for (i = 1; i < fs->fs_ncg; i *= 2) { cg += i; if (cg >= fs->fs_ncg) cg -= fs->fs_ncg; result = (*allocator)(ip, cg, 0, size); if (result) return (result); } /* * 3: brute force search * Note that we start at i == 2, since 0 was checked initially, * and 1 is always checked in the quadratic rehash. */ cg = (icg + 2) % fs->fs_ncg; for (i = 2; i < fs->fs_ncg; i++) { result = (*allocator)(ip, cg, 0, size); if (result) return (result); cg++; if (cg == fs->fs_ncg) cg = 0; } return (0); } /* * Determine whether a fragment can be extended. * * Check to see if the necessary fragments are available, and * if they are, allocate them. */ static daddr_t ffs_fragextend(ip, cg, bprev, osize, nsize) struct inode *ip; int cg; long bprev; int osize, nsize; { register struct fs *fs; register struct cg *cgp; struct buf *bp; long bno; int frags, bbase; int i, error; fs = ip->i_fs; if (fs->fs_cs(fs, cg).cs_nffree < numfrags(fs, nsize - osize)) return (0); frags = numfrags(fs, nsize); bbase = fragnum(fs, bprev); if (bbase > fragnum(fs, (bprev + frags - 1))) { /* cannot extend across a block boundary */ return (0); } error = bread(ip->i_devvp, fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, NOCRED, &bp); if (error) { brelse(bp); return (0); } cgp = (struct cg *)bp->b_data; if (!cg_chkmagic(cgp)) { brelse(bp); return (0); } cgp->cg_time = time.tv_sec; bno = dtogd(fs, bprev); for (i = numfrags(fs, osize); i < frags; i++) if (isclr(cg_blksfree(cgp), bno + i)) { brelse(bp); return (0); } /* * the current fragment can be extended * deduct the count on fragment being extended into * increase the count on the remaining fragment (if any) * allocate the extended piece */ for (i = frags; i < fs->fs_frag - bbase; i++) if (isclr(cg_blksfree(cgp), bno + i)) break; cgp->cg_frsum[i - numfrags(fs, osize)]--; if (i != frags) cgp->cg_frsum[i - frags]++; for (i = numfrags(fs, osize); i < frags; i++) { clrbit(cg_blksfree(cgp), bno + i); cgp->cg_cs.cs_nffree--; fs->fs_cstotal.cs_nffree--; fs->fs_cs(fs, cg).cs_nffree--; } fs->fs_fmod = 1; if (DOINGSOFTDEP(ITOV(ip))) softdep_setup_blkmapdep(bp, fs, bprev); bdwrite(bp); return (bprev); } /* * Determine whether a block can be allocated. * * Check to see if a block of the appropriate size is available, * and if it is, allocate it. */ static daddr_t ffs_alloccg(ip, cg, bpref, size) struct inode *ip; int cg; daddr_t bpref; int size; { register struct fs *fs; register struct cg *cgp; struct buf *bp; daddr_t bno, blkno; int error, i, frags, allocsiz; fs = ip->i_fs; if (fs->fs_cs(fs, cg).cs_nbfree == 0 && size == fs->fs_bsize) return (0); error = bread(ip->i_devvp, fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, NOCRED, &bp); if (error) { brelse(bp); return (0); } cgp = (struct cg *)bp->b_data; if (!cg_chkmagic(cgp) || (cgp->cg_cs.cs_nbfree == 0 && size == fs->fs_bsize)) { brelse(bp); return (0); } cgp->cg_time = time.tv_sec; if (size == fs->fs_bsize) { bno = ffs_alloccgblk(ip, bp, bpref); bdwrite(bp); return (bno); } /* * check to see if any fragments are already available * allocsiz is the size which will be allocated, hacking * it down to a smaller size if necessary */ frags = numfrags(fs, size); for (allocsiz = frags; allocsiz < fs->fs_frag; allocsiz++) if (cgp->cg_frsum[allocsiz] != 0) break; if (allocsiz == fs->fs_frag) { /* * no fragments were available, so a block will be * allocated, and hacked up */ if (cgp->cg_cs.cs_nbfree == 0) { brelse(bp); return (0); } bno = ffs_alloccgblk(ip, bp, bpref); bpref = dtogd(fs, bno); for (i = frags; i < fs->fs_frag; i++) setbit(cg_blksfree(cgp), bpref + i); i = fs->fs_frag - frags; cgp->cg_cs.cs_nffree += i; fs->fs_cstotal.cs_nffree += i; fs->fs_cs(fs, cg).cs_nffree += i; fs->fs_fmod = 1; cgp->cg_frsum[i]++; bdwrite(bp); return (bno); } bno = ffs_mapsearch(fs, cgp, bpref, allocsiz); if (bno < 0) { brelse(bp); return (0); } for (i = 0; i < frags; i++) clrbit(cg_blksfree(cgp), bno + i); cgp->cg_cs.cs_nffree -= frags; fs->fs_cstotal.cs_nffree -= frags; fs->fs_cs(fs, cg).cs_nffree -= frags; fs->fs_fmod = 1; cgp->cg_frsum[allocsiz]--; if (frags != allocsiz) cgp->cg_frsum[allocsiz - frags]++; blkno = cg * fs->fs_fpg + bno; if (DOINGSOFTDEP(ITOV(ip))) softdep_setup_blkmapdep(bp, fs, blkno); bdwrite(bp); return ((u_long)blkno); } /* * Allocate a block in a cylinder group. * * This algorithm implements the following policy: * 1) allocate the requested block. * 2) allocate a rotationally optimal block in the same cylinder. * 3) allocate the next available block on the block rotor for the * specified cylinder group. * Note that this routine only allocates fs_bsize blocks; these * blocks may be fragmented by the routine that allocates them. */ static daddr_t ffs_alloccgblk(ip, bp, bpref) struct inode *ip; struct buf *bp; daddr_t bpref; { struct fs *fs; struct cg *cgp; daddr_t bno, blkno; int cylno, pos, delta; short *cylbp; register int i; fs = ip->i_fs; cgp = (struct cg *)bp->b_data; if (bpref == 0 || dtog(fs, bpref) != cgp->cg_cgx) { bpref = cgp->cg_rotor; goto norot; } bpref = blknum(fs, bpref); bpref = dtogd(fs, bpref); /* * if the requested block is available, use it */ if (ffs_isblock(fs, cg_blksfree(cgp), fragstoblks(fs, bpref))) { bno = bpref; goto gotit; } if (fs->fs_cpc == 0 || fs->fs_nrpos <= 1) { /* * Block layout information is not available. * Leaving bpref unchanged means we take the * next available free block following the one * we just allocated. Hopefully this will at * least hit a track cache on drives of unknown * geometry (e.g. SCSI). */ goto norot; } /* * check for a block available on the same cylinder */ cylno = cbtocylno(fs, bpref); if (cg_blktot(cgp)[cylno] == 0) goto norot; /* * check the summary information to see if a block is * available in the requested cylinder starting at the * requested rotational position and proceeding around. */ cylbp = cg_blks(fs, cgp, cylno); pos = cbtorpos(fs, bpref); for (i = pos; i < fs->fs_nrpos; i++) if (cylbp[i] > 0) break; if (i == fs->fs_nrpos) for (i = 0; i < pos; i++) if (cylbp[i] > 0) break; if (cylbp[i] > 0) { /* * found a rotational position, now find the actual * block. A panic if none is actually there. */ pos = cylno % fs->fs_cpc; bno = (cylno - pos) * fs->fs_spc / NSPB(fs); if (fs_postbl(fs, pos)[i] == -1) { printf("pos = %d, i = %d, fs = %s\n", pos, i, fs->fs_fsmnt); panic("ffs_alloccgblk: cyl groups corrupted"); } for (i = fs_postbl(fs, pos)[i];; ) { if (ffs_isblock(fs, cg_blksfree(cgp), bno + i)) { bno = blkstofrags(fs, (bno + i)); goto gotit; } delta = fs_rotbl(fs)[i]; if (delta <= 0 || delta + i > fragstoblks(fs, fs->fs_fpg)) break; i += delta; } printf("pos = %d, i = %d, fs = %s\n", pos, i, fs->fs_fsmnt); panic("ffs_alloccgblk: can't find blk in cyl"); } norot: /* * no blocks in the requested cylinder, so take next * available one in this cylinder group. */ bno = ffs_mapsearch(fs, cgp, bpref, (int)fs->fs_frag); if (bno < 0) return (0); cgp->cg_rotor = bno; gotit: blkno = fragstoblks(fs, bno); ffs_clrblock(fs, cg_blksfree(cgp), (long)blkno); ffs_clusteracct(fs, cgp, blkno, -1); cgp->cg_cs.cs_nbfree--; fs->fs_cstotal.cs_nbfree--; fs->fs_cs(fs, cgp->cg_cgx).cs_nbfree--; cylno = cbtocylno(fs, bno); cg_blks(fs, cgp, cylno)[cbtorpos(fs, bno)]--; cg_blktot(cgp)[cylno]--; fs->fs_fmod = 1; blkno = cgp->cg_cgx * fs->fs_fpg + bno; if (DOINGSOFTDEP(ITOV(ip))) softdep_setup_blkmapdep(bp, fs, blkno); return (blkno); } /* * Determine whether a cluster can be allocated. * * We do not currently check for optimal rotational layout if there * are multiple choices in the same cylinder group. Instead we just * take the first one that we find following bpref. */ static daddr_t ffs_clusteralloc(ip, cg, bpref, len) struct inode *ip; int cg; daddr_t bpref; int len; { register struct fs *fs; register struct cg *cgp; struct buf *bp; int i, got, run, bno, bit, map; u_char *mapp; int32_t *lp; fs = ip->i_fs; if (fs->fs_maxcluster[cg] < len) return (0); if (bread(ip->i_devvp, fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, NOCRED, &bp)) goto fail; cgp = (struct cg *)bp->b_data; if (!cg_chkmagic(cgp)) goto fail; /* * Check to see if a cluster of the needed size (or bigger) is * available in this cylinder group. */ lp = &cg_clustersum(cgp)[len]; for (i = len; i <= fs->fs_contigsumsize; i++) if (*lp++ > 0) break; if (i > fs->fs_contigsumsize) { /* * This is the first time looking for a cluster in this * cylinder group. Update the cluster summary information * to reflect the true maximum sized cluster so that * future cluster allocation requests can avoid reading * the cylinder group map only to find no clusters. */ lp = &cg_clustersum(cgp)[len - 1]; for (i = len - 1; i > 0; i--) if (*lp-- > 0) break; fs->fs_maxcluster[cg] = i; goto fail; } /* * Search the cluster map to find a big enough cluster. * We take the first one that we find, even if it is larger * than we need as we prefer to get one close to the previous * block allocation. We do not search before the current * preference point as we do not want to allocate a block * that is allocated before the previous one (as we will * then have to wait for another pass of the elevator * algorithm before it will be read). We prefer to fail and * be recalled to try an allocation in the next cylinder group. */ if (dtog(fs, bpref) != cg) bpref = 0; else bpref = fragstoblks(fs, dtogd(fs, blknum(fs, bpref))); mapp = &cg_clustersfree(cgp)[bpref / NBBY]; map = *mapp++; bit = 1 << (bpref % NBBY); for (run = 0, got = bpref; got < cgp->cg_nclusterblks; got++) { if ((map & bit) == 0) { run = 0; } else { run++; if (run == len) break; } if ((got & (NBBY - 1)) != (NBBY - 1)) { bit <<= 1; } else { map = *mapp++; bit = 1; } } if (got >= cgp->cg_nclusterblks) goto fail; /* * Allocate the cluster that we have found. */ #ifdef DIAGNOSTIC for (i = 1; i <= len; i++) if (!ffs_isblock(fs, cg_blksfree(cgp), got - run + i)) panic("ffs_clusteralloc: map mismatch"); #endif bno = cg * fs->fs_fpg + blkstofrags(fs, got - run + 1); #ifdef DIAGNOSTIC if (dtog(fs, bno) != cg) panic("ffs_clusteralloc: allocated out of group"); #endif len = blkstofrags(fs, len); for (i = 0; i < len; i += fs->fs_frag) if (ffs_alloccgblk(ip, bp, bno + i) != bno + i) panic("ffs_clusteralloc: lost block"); bdwrite(bp); return (bno); fail: brelse(bp); return (0); } /* * Determine whether an inode can be allocated. * * Check to see if an inode is available, and if it is, * allocate it using the following policy: * 1) allocate the requested inode. * 2) allocate the next available inode after the requested * inode in the specified cylinder group. */ static daddr_t ffs_nodealloccg(ip, cg, ipref, mode) struct inode *ip; int cg; daddr_t ipref; int mode; { register struct fs *fs; register struct cg *cgp; struct buf *bp; int error, start, len, loc, map, i; fs = ip->i_fs; if (fs->fs_cs(fs, cg).cs_nifree == 0) return (0); error = bread(ip->i_devvp, fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, NOCRED, &bp); if (error) { brelse(bp); return (0); } cgp = (struct cg *)bp->b_data; if (!cg_chkmagic(cgp) || cgp->cg_cs.cs_nifree == 0) { brelse(bp); return (0); } cgp->cg_time = time.tv_sec; if (ipref) { ipref %= fs->fs_ipg; if (isclr(cg_inosused(cgp), ipref)) goto gotit; } start = cgp->cg_irotor / NBBY; len = howmany(fs->fs_ipg - cgp->cg_irotor, NBBY); loc = skpc(0xff, len, &cg_inosused(cgp)[start]); if (loc == 0) { len = start + 1; start = 0; loc = skpc(0xff, len, &cg_inosused(cgp)[0]); if (loc == 0) { printf("cg = %d, irotor = %d, fs = %s\n", cg, cgp->cg_irotor, fs->fs_fsmnt); panic("ffs_nodealloccg: map corrupted"); /* NOTREACHED */ } } i = start + len - loc; map = cg_inosused(cgp)[i]; ipref = i * NBBY; for (i = 1; i < (1 << NBBY); i <<= 1, ipref++) { if ((map & i) == 0) { cgp->cg_irotor = ipref; goto gotit; } } printf("fs = %s\n", fs->fs_fsmnt); panic("ffs_nodealloccg: block not in map"); /* NOTREACHED */ gotit: if (DOINGSOFTDEP(ITOV(ip))) softdep_setup_inomapdep(bp, ip, cg * fs->fs_ipg + ipref); setbit(cg_inosused(cgp), ipref); cgp->cg_cs.cs_nifree--; fs->fs_cstotal.cs_nifree--; fs->fs_cs(fs, cg).cs_nifree--; fs->fs_fmod = 1; if ((mode & IFMT) == IFDIR) { cgp->cg_cs.cs_ndir++; fs->fs_cstotal.cs_ndir++; fs->fs_cs(fs, cg).cs_ndir++; } bdwrite(bp); return (cg * fs->fs_ipg + ipref); } /* * Free a block or fragment. * * The specified block or fragment is placed back in the * free map. If a fragment is deallocated, a possible * block reassembly is checked. */ void ffs_blkfree(ip, bno, size) register struct inode *ip; daddr_t bno; long size; { register struct fs *fs; register struct cg *cgp; struct buf *bp; daddr_t blkno; int i, error, cg, blk, frags, bbase; fs = ip->i_fs; if ((u_int)size > fs->fs_bsize || fragoff(fs, size) != 0 || fragnum(fs, bno) + numfrags(fs, size) > fs->fs_frag) { printf("dev = 0x%x, bsize = %d, size = %ld, fs = %s\n", ip->i_dev, fs->fs_bsize, size, fs->fs_fsmnt); panic("ffs_blkfree: bad size"); } cg = dtog(fs, bno); if ((u_int)bno >= fs->fs_size) { printf("bad block %d, ino %u\n", bno, ip->i_number); ffs_fserr(fs, ip->i_ffs_uid, "bad block"); return; } error = bread(ip->i_devvp, fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, NOCRED, &bp); if (error) { brelse(bp); return; } cgp = (struct cg *)bp->b_data; if (!cg_chkmagic(cgp)) { brelse(bp); return; } cgp->cg_time = time.tv_sec; bno = dtogd(fs, bno); if (size == fs->fs_bsize) { blkno = fragstoblks(fs, bno); if (!ffs_isfreeblock(fs, cg_blksfree(cgp), blkno)) { printf("dev = 0x%x, block = %d, fs = %s\n", ip->i_dev, bno, fs->fs_fsmnt); panic("ffs_blkfree: freeing free block"); } ffs_setblock(fs, cg_blksfree(cgp), blkno); ffs_clusteracct(fs, cgp, blkno, 1); cgp->cg_cs.cs_nbfree++; fs->fs_cstotal.cs_nbfree++; fs->fs_cs(fs, cg).cs_nbfree++; i = cbtocylno(fs, bno); cg_blks(fs, cgp, i)[cbtorpos(fs, bno)]++; cg_blktot(cgp)[i]++; } else { bbase = bno - fragnum(fs, bno); /* * decrement the counts associated with the old frags */ blk = blkmap(fs, cg_blksfree(cgp), bbase); ffs_fragacct(fs, blk, cgp->cg_frsum, -1); /* * deallocate the fragment */ frags = numfrags(fs, size); for (i = 0; i < frags; i++) { if (isset(cg_blksfree(cgp), bno + i)) { printf("dev = 0x%x, block = %d, fs = %s\n", ip->i_dev, bno + i, fs->fs_fsmnt); panic("ffs_blkfree: freeing free frag"); } setbit(cg_blksfree(cgp), bno + i); } cgp->cg_cs.cs_nffree += i; fs->fs_cstotal.cs_nffree += i; fs->fs_cs(fs, cg).cs_nffree += i; /* * add back in counts associated with the new frags */ blk = blkmap(fs, cg_blksfree(cgp), bbase); ffs_fragacct(fs, blk, cgp->cg_frsum, 1); /* * if a complete block has been reassembled, account for it */ blkno = fragstoblks(fs, bbase); if (ffs_isblock(fs, cg_blksfree(cgp), blkno)) { cgp->cg_cs.cs_nffree -= fs->fs_frag; fs->fs_cstotal.cs_nffree -= fs->fs_frag; fs->fs_cs(fs, cg).cs_nffree -= fs->fs_frag; ffs_clusteracct(fs, cgp, blkno, 1); cgp->cg_cs.cs_nbfree++; fs->fs_cstotal.cs_nbfree++; fs->fs_cs(fs, cg).cs_nbfree++; i = cbtocylno(fs, bbase); cg_blks(fs, cgp, i)[cbtorpos(fs, bbase)]++; cg_blktot(cgp)[i]++; } } fs->fs_fmod = 1; bdwrite(bp); } int ffs_inode_free(struct inode *pip, ino_t ino, mode_t mode) { struct vnode *pvp = ITOV(pip); if (DOINGSOFTDEP(pvp)) { softdep_freefile(pvp, ino, mode); return (0); } return (ffs_freefile(pip, ino, mode)); } /* * Do the actual free operation. * The specified inode is placed back in the free map. */ int ffs_freefile(struct inode *pip, ino_t ino, mode_t mode) { struct fs *fs; struct cg *cgp; struct buf *bp; int error, cg; fs = pip->i_fs; if ((u_int)ino >= fs->fs_ipg * fs->fs_ncg) panic("ffs_freefile: range: dev = 0x%x, ino = %d, fs = %s", pip->i_dev, ino, fs->fs_fsmnt); cg = ino_to_cg(fs, ino); error = bread(pip->i_devvp, fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, NOCRED, &bp); if (error) { brelse(bp); return (error); } cgp = (struct cg *)bp->b_data; if (!cg_chkmagic(cgp)) { brelse(bp); return (0); } cgp->cg_time = time.tv_sec; ino %= fs->fs_ipg; if (isclr(cg_inosused(cgp), ino)) { printf("dev = 0x%x, ino = %u, fs = %s\n", pip->i_dev, ino, fs->fs_fsmnt); if (fs->fs_ronly == 0) panic("ffs_freefile: freeing free inode"); } clrbit(cg_inosused(cgp), ino); if (ino < cgp->cg_irotor) cgp->cg_irotor = ino; cgp->cg_cs.cs_nifree++; fs->fs_cstotal.cs_nifree++; fs->fs_cs(fs, cg).cs_nifree++; if ((mode & IFMT) == IFDIR) { cgp->cg_cs.cs_ndir--; fs->fs_cstotal.cs_ndir--; fs->fs_cs(fs, cg).cs_ndir--; } fs->fs_fmod = 1; bdwrite(bp); return (0); } #ifdef DIAGNOSTIC /* * Verify allocation of a block or fragment. Returns true if block or * fragment is allocated, false if it is free. */ static int ffs_checkblk(ip, bno, size) struct inode *ip; daddr_t bno; long size; { struct fs *fs; struct cg *cgp; struct buf *bp; int i, error, frags, free; fs = ip->i_fs; if ((u_int)size > fs->fs_bsize || fragoff(fs, size) != 0) { printf("bsize = %d, size = %ld, fs = %s\n", fs->fs_bsize, size, fs->fs_fsmnt); panic("ffs_checkblk: bad size"); } if ((u_int)bno >= fs->fs_size) panic("ffs_checkblk: bad block %d", bno); error = bread(ip->i_devvp, fsbtodb(fs, cgtod(fs, dtog(fs, bno))), (int)fs->fs_cgsize, NOCRED, &bp); if (error) { brelse(bp); return (0); } cgp = (struct cg *)bp->b_data; if (!cg_chkmagic(cgp)) { brelse(bp); return (0); } bno = dtogd(fs, bno); if (size == fs->fs_bsize) { free = ffs_isblock(fs, cg_blksfree(cgp), fragstoblks(fs, bno)); } else { frags = numfrags(fs, size); for (free = 0, i = 0; i < frags; i++) if (isset(cg_blksfree(cgp), bno + i)) free++; if (free != 0 && free != frags) panic("ffs_checkblk: partially free fragment"); } brelse(bp); return (!free); } #endif /* DIAGNOSTIC */ /* * Find a block of the specified size in the specified cylinder group. * * It is a panic if a request is made to find a block if none are * available. */ static daddr_t ffs_mapsearch(fs, cgp, bpref, allocsiz) register struct fs *fs; register struct cg *cgp; daddr_t bpref; int allocsiz; { daddr_t bno; int start, len, loc, i; int blk, field, subfield, pos; /* * find the fragment by searching through the free block * map for an appropriate bit pattern */ if (bpref) start = dtogd(fs, bpref) / NBBY; else start = cgp->cg_frotor / NBBY; len = howmany(fs->fs_fpg, NBBY) - start; loc = scanc((u_int)len, (u_char *)&cg_blksfree(cgp)[start], (u_char *)fragtbl[fs->fs_frag], (u_char)(1 << (allocsiz - 1 + (fs->fs_frag % NBBY)))); if (loc == 0) { len = start + 1; start = 0; loc = scanc((u_int)len, (u_char *)&cg_blksfree(cgp)[0], (u_char *)fragtbl[fs->fs_frag], (u_char)(1 << (allocsiz - 1 + (fs->fs_frag % NBBY)))); if (loc == 0) { printf("start = %d, len = %d, fs = %s\n", start, len, fs->fs_fsmnt); panic("ffs_alloccg: map corrupted"); /* NOTREACHED */ } } bno = (start + len - loc) * NBBY; cgp->cg_frotor = bno; /* * found the byte in the map * sift through the bits to find the selected frag */ for (i = bno + NBBY; bno < i; bno += fs->fs_frag) { blk = blkmap(fs, cg_blksfree(cgp), bno); blk <<= 1; field = around[allocsiz]; subfield = inside[allocsiz]; for (pos = 0; pos <= fs->fs_frag - allocsiz; pos++) { if ((blk & field) == subfield) return (bno + pos); field <<= 1; subfield <<= 1; } } printf("bno = %d, fs = %s\n", bno, fs->fs_fsmnt); panic("ffs_alloccg: block not in map"); return (-1); } /* * Update the cluster map because of an allocation or free. * * Cnt == 1 means free; cnt == -1 means allocating. */ void ffs_clusteracct(fs, cgp, blkno, cnt) struct fs *fs; struct cg *cgp; daddr_t blkno; int cnt; { int32_t *sump; int32_t *lp; u_char *freemapp, *mapp; int i, start, end, forw, back, map, bit; if (fs->fs_contigsumsize <= 0) return; freemapp = cg_clustersfree(cgp); sump = cg_clustersum(cgp); /* * Allocate or clear the actual block. */ if (cnt > 0) setbit(freemapp, blkno); else clrbit(freemapp, blkno); /* * Find the size of the cluster going forward. */ start = blkno + 1; end = start + fs->fs_contigsumsize; if (end >= cgp->cg_nclusterblks) end = cgp->cg_nclusterblks; mapp = &freemapp[start / NBBY]; map = *mapp++; bit = 1 << (start % NBBY); for (i = start; i < end; i++) { if ((map & bit) == 0) break; if ((i & (NBBY - 1)) != (NBBY - 1)) { bit <<= 1; } else { map = *mapp++; bit = 1; } } forw = i - start; /* * Find the size of the cluster going backward. */ start = blkno - 1; end = start - fs->fs_contigsumsize; if (end < 0) end = -1; mapp = &freemapp[start / NBBY]; map = *mapp--; bit = 1 << (start % NBBY); for (i = start; i > end; i--) { if ((map & bit) == 0) break; if ((i & (NBBY - 1)) != 0) { bit >>= 1; } else { map = *mapp--; bit = 1 << (NBBY - 1); } } back = start - i; /* * Account for old cluster and the possibly new forward and * back clusters. */ i = back + forw + 1; if (i > fs->fs_contigsumsize) i = fs->fs_contigsumsize; sump[i] += cnt; if (back > 0) sump[back] -= cnt; if (forw > 0) sump[forw] -= cnt; /* * Update cluster summary information. */ lp = &sump[fs->fs_contigsumsize]; for (i = fs->fs_contigsumsize; i > 0; i--) if (*lp-- > 0) break; fs->fs_maxcluster[cgp->cg_cgx] = i; } /* * Fserr prints the name of a filesystem with an error diagnostic. * * The form of the error message is: * fs: error message */ static void ffs_fserr(fs, uid, cp) struct fs *fs; u_int uid; char *cp; { log(LOG_ERR, "uid %u on %s: %s\n", uid, fs->fs_fsmnt, cp); } makefs/src/sys/ufs/ffs/ffs_balloc.c010064400000000000000000000241111314214547200144730ustar00/* $OpenBSD: ffs_balloc.c,v 1.24 2003/06/02 23:28:22 millert Exp $ */ /* $NetBSD: ffs_balloc.c,v 1.3 1996/02/09 22:22:21 christos Exp $ */ /* * Copyright (c) 1982, 1986, 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. * * @(#)ffs_balloc.c 8.4 (Berkeley) 9/23/93 */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Balloc defines the structure of filesystem storage * by allocating the physical blocks on a device given * the inode and the logical block number in a file. */ int ffs_balloc(struct inode *ip, off_t startoffset, int size, struct ucred *cred, int flags, struct buf **bpp) { daddr_t lbn; struct fs *fs; daddr_t nb; struct buf *bp, *nbp; struct vnode *vp; struct indir indirs[NIADDR + 2]; daddr_t newb, *bap, pref; int deallocated, osize, nsize, num, i, error; daddr_t *allocib, *blkp, *allocblk, allociblk[NIADDR+1]; int unwindidx = -1; vp = ITOV(ip); fs = ip->i_fs; lbn = lblkno(fs, startoffset); size = blkoff(fs, startoffset) + size; if (size > fs->fs_bsize) panic("ffs_balloc: blk too big"); if (bpp != NULL) *bpp = NULL; if (lbn < 0) return (EFBIG); /* * If the next write will extend the file into a new block, * and the file is currently composed of a fragment * this fragment has to be extended to be a full block. */ nb = lblkno(fs, ip->i_ffs_size); if (nb < NDADDR && nb < lbn) { osize = blksize(fs, ip, nb); if (osize < fs->fs_bsize && osize > 0) { error = ffs_realloccg(ip, nb, ffs_blkpref(ip, nb, (int)nb, &ip->i_ffs_db[0]), osize, (int)fs->fs_bsize, cred, bpp, &newb); if (error) return (error); if (DOINGSOFTDEP(vp)) softdep_setup_allocdirect(ip, nb, newb, ip->i_ffs_db[nb], fs->fs_bsize, osize, bpp ? *bpp : NULL); ip->i_ffs_size = lblktosize(fs, nb + 1); uvm_vnp_setsize(vp, ip->i_ffs_size); ip->i_ffs_db[nb] = newb; ip->i_flag |= IN_CHANGE | IN_UPDATE; if (bpp != NULL) { if (flags & B_SYNC) bwrite(*bpp); else bawrite(*bpp); } } } /* * The first NDADDR blocks are direct blocks */ if (lbn < NDADDR) { nb = ip->i_ffs_db[lbn]; if (nb != 0 && ip->i_ffs_size >= lblktosize(fs, lbn + 1)) { /* * The block is an already-allocated direct block * and the file already extends past this block, * thus this must be a whole block. * Just read the block (if requested). */ if (bpp != NULL) { error = bread(vp, lbn, fs->fs_bsize, NOCRED, bpp); if (error) { brelse(*bpp); return (error); } } return (0); } if (nb != 0) { /* * Consider need to reallocate a fragment. */ osize = fragroundup(fs, blkoff(fs, ip->i_ffs_size)); nsize = fragroundup(fs, size); if (nsize <= osize) { /* * The existing block is already * at least as big as we want. * Just read the block (if requested). */ if (bpp != NULL) { error = bread(vp, lbn, osize, NOCRED, bpp); if (error) { brelse(*bpp); return (error); } } return (0); } else { /* * The existing block is smaller than we * want, grow it. */ error = ffs_realloccg(ip, lbn, ffs_blkpref(ip, lbn, (int)lbn, &ip->i_ffs_db[0]), osize, nsize, cred, bpp, &newb); if (error) return (error); if (DOINGSOFTDEP(vp)) softdep_setup_allocdirect(ip, lbn, newb, nb, nsize, osize, bpp ? *bpp : NULL); } } else { /* * The block was not previously allocated, * allocate a new block or fragment. */ if (ip->i_ffs_size < lblktosize(fs, lbn + 1)) nsize = fragroundup(fs, size); else nsize = fs->fs_bsize; error = ffs_alloc(ip, lbn, ffs_blkpref(ip, lbn, (int)lbn, &ip->i_ffs_db[0]), nsize, cred, &newb); if (error) return (error); if (bpp != NULL) { *bpp = getblk(vp, lbn, nsize, 0, 0); (*bpp)->b_blkno = fsbtodb(fs, newb); if (flags & B_CLRBUF) clrbuf(*bpp); } if (DOINGSOFTDEP(vp)) softdep_setup_allocdirect(ip, lbn, newb, 0, nsize, 0, bpp ? *bpp : NULL); } ip->i_ffs_db[lbn] = newb; ip->i_flag |= IN_CHANGE | IN_UPDATE; return (0); } /* * Determine the number of levels of indirection. */ pref = 0; if ((error = ufs_getlbns(vp, lbn, indirs, &num)) != 0) return(error); #ifdef DIAGNOSTIC if (num < 1) panic ("ffs_balloc: ufs_bmaparray returned indirect block"); #endif /* * Fetch the first indirect block allocating if necessary. */ --num; nb = ip->i_ffs_ib[indirs[0].in_off]; allocib = NULL; allocblk = allociblk; if (nb == 0) { pref = ffs_blkpref(ip, lbn, 0, (daddr_t *)0); error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, cred, &newb); if (error) goto fail; nb = newb; *allocblk++ = nb; bp = getblk(vp, indirs[1].in_lbn, fs->fs_bsize, 0, 0); bp->b_blkno = fsbtodb(fs, nb); clrbuf(bp); if (DOINGSOFTDEP(vp)) { softdep_setup_allocdirect(ip, NDADDR + indirs[0].in_off, newb, 0, fs->fs_bsize, 0, bp); bdwrite(bp); } else { /* * Write synchronously so that indirect blocks * never point at garbage. */ if ((error = bwrite(bp)) != 0) goto fail; } allocib = &ip->i_ffs_ib[indirs[0].in_off]; *allocib = nb; ip->i_flag |= IN_CHANGE | IN_UPDATE; } /* * Fetch through the indirect blocks, allocating as necessary. */ for (i = 1;;) { error = bread(vp, indirs[i].in_lbn, (int)fs->fs_bsize, NOCRED, &bp); if (error) { brelse(bp); goto fail; } bap = (daddr_t *)bp->b_data; nb = bap[indirs[i].in_off]; if (i == num) break; i++; if (nb != 0) { brelse(bp); continue; } if (pref == 0) pref = ffs_blkpref(ip, lbn, 0, (daddr_t *)0); error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, cred, &newb); if (error) { brelse(bp); goto fail; } nb = newb; *allocblk++ = nb; nbp = getblk(vp, indirs[i].in_lbn, fs->fs_bsize, 0, 0); nbp->b_blkno = fsbtodb(fs, nb); clrbuf(nbp); if (DOINGSOFTDEP(vp)) { softdep_setup_allocindir_meta(nbp, ip, bp, indirs[i - 1].in_off, nb); bdwrite(nbp); } else { /* * Write synchronously so that indirect blocks * never point at garbage. */ if ((error = bwrite(nbp)) != 0) { brelse(bp); goto fail; } } bap[indirs[i - 1].in_off] = nb; if (allocib == NULL && unwindidx < 0) unwindidx = i - 1; /* * If required, write synchronously, otherwise use * delayed write. */ if (flags & B_SYNC) { bwrite(bp); } else { bdwrite(bp); } } /* * Get the data block, allocating if necessary. */ if (nb == 0) { pref = ffs_blkpref(ip, lbn, indirs[i].in_off, &bap[0]); error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, cred, &newb); if (error) { brelse(bp); goto fail; } nb = newb; *allocblk++ = nb; if (bpp != NULL) { nbp = getblk(vp, lbn, fs->fs_bsize, 0, 0); nbp->b_blkno = fsbtodb(fs, nb); if (flags & B_CLRBUF) clrbuf(nbp); *bpp = nbp; } if (DOINGSOFTDEP(vp)) softdep_setup_allocindir_page(ip, lbn, bp, indirs[i].in_off, nb, 0, bpp ? *bpp : NULL); bap[indirs[i].in_off] = nb; /* * If required, write synchronously, otherwise use * delayed write. */ if (flags & B_SYNC) { bwrite(bp); } else { bdwrite(bp); } return (0); } brelse(bp); if (bpp != NULL) { if (flags & B_CLRBUF) { error = bread(vp, lbn, (int)fs->fs_bsize, NOCRED, &nbp); if (error) { brelse(nbp); goto fail; } } else { nbp = getblk(vp, lbn, fs->fs_bsize, 0, 0); nbp->b_blkno = fsbtodb(fs, nb); } *bpp = nbp; } return (0); fail: /* * If we have failed part way through block allocation, we * have to deallocate any indirect blocks that we have allocated. */ for (deallocated = 0, blkp = allociblk; blkp < allocblk; blkp++) { ffs_blkfree(ip, *blkp, fs->fs_bsize); deallocated += fs->fs_bsize; } if (allocib != NULL) { *allocib = 0; } else if (unwindidx >= 0) { int r; r = bread(vp, indirs[unwindidx].in_lbn, (int)fs->fs_bsize, NOCRED, &bp); if (r) panic("Could not unwind indirect block, error %d", r); bap = (daddr_t *)bp->b_data; bap[indirs[unwindidx].in_off] = 0; if (flags & B_SYNC) { bwrite(bp); } else { bdwrite(bp); } } if (deallocated) { /* * Restore user's disk quota because allocation failed. */ (void)ufs_quota_free_blocks(ip, btodb(deallocated), cred); ip->i_ffs_blocks -= btodb(deallocated); ip->i_flag |= IN_CHANGE | IN_UPDATE; } return (error); } makefs/src/sys/ufs/ffs/ffs_extern.h010064400000000000000000000174701020120120400145360ustar00/* $OpenBSD: ffs_extern.h,v 1.24 2004/07/13 21:04:29 millert Exp $ */ /* $NetBSD: ffs_extern.h,v 1.4 1996/02/09 22:22:22 christos Exp $ */ /*- * Copyright (c) 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. * * @(#)ffs_extern.h 8.3 (Berkeley) 4/16/94 */ #define FFS_CLUSTERREAD 1 /* cluster reading enabled */ #define FFS_CLUSTERWRITE 2 /* cluster writing enabled */ #define FFS_REALLOCBLKS 3 /* block reallocation enabled */ #define FFS_ASYNCFREE 4 /* asynchronous block freeing enabled */ #define FFS_MAX_SOFTDEPS 5 /* maximum structs before slowdown */ #define FFS_SD_TICKDELAY 6 /* ticks to pause during slowdown */ #define FFS_SD_WORKLIST_PUSH 7 /* # of worklist cleanups */ #define FFS_SD_BLK_LIMIT_PUSH 8 /* # of times block limit neared */ #define FFS_SD_INO_LIMIT_PUSH 9 /* # of times inode limit neared */ #define FFS_SD_BLK_LIMIT_HIT 10 /* # of times block slowdown imposed */ #define FFS_SD_INO_LIMIT_HIT 11 /* # of times inode slowdown imposed */ #define FFS_SD_SYNC_LIMIT_HIT 12 /* # of synchronous slowdowns imposed */ #define FFS_SD_INDIR_BLK_PTRS 13 /* bufs redirtied as indir ptrs not written */ #define FFS_SD_INODE_BITMAP 14 /* bufs redirtied as inode bitmap not written */ #define FFS_SD_DIRECT_BLK_PTRS 15 /* bufs redirtied as direct ptrs not written */ #define FFS_SD_DIR_ENTRY 16 /* bufs redirtied as dir entry cannot write */ #define FFS_DIRHASH_DIRSIZE 17 /* min directory size, in bytes */ #define FFS_DIRHASH_MAXMEM 18 /* max kvm to use, in bytes */ #define FFS_DIRHASH_MEM 19 /* current mem usage, in bytes */ #define FFS_MAXID 20 /* number of valid ffs ids */ #define FFS_NAMES { \ { 0, 0 }, \ { "doclusterread", CTLTYPE_INT }, \ { "doclusterwrite", CTLTYPE_INT }, \ { "doreallocblks", CTLTYPE_INT }, \ { "doasyncfree", CTLTYPE_INT }, \ { "max_softdeps", CTLTYPE_INT }, \ { "sd_tickdelay", CTLTYPE_INT }, \ { "sd_worklist_push", CTLTYPE_INT }, \ { "sd_blk_limit_push", CTLTYPE_INT }, \ { "sd_ino_limit_push", CTLTYPE_INT }, \ { "sd_blk_limit_hit", CTLTYPE_INT }, \ { "sd_ino_limit_hit", CTLTYPE_INT }, \ { "sd_sync_limit_hit", CTLTYPE_INT }, \ { "sd_indir_blk_ptrs", CTLTYPE_INT }, \ { "sd_inode_bitmap", CTLTYPE_INT }, \ { "sd_direct_blk_ptrs", CTLTYPE_INT }, \ { "sd_dir_entry", CTLTYPE_INT }, \ { "dirhash_dirsize", CTLTYPE_INT }, \ { "dirhash_maxmem", CTLTYPE_INT }, \ { "dirhash_mem", CTLTYPE_INT }, \ } struct buf; struct fid; struct fs; struct inode; struct mount; struct nameidata; struct proc; struct statfs; struct timeval; struct ucred; struct ufsmount; struct vfsconf; struct uio; struct vnode; struct mbuf; struct cg; struct vop_vfree_args; __BEGIN_DECLS /* ffs_alloc.c */ int ffs_alloc(struct inode *, daddr_t, daddr_t , int, struct ucred *, daddr_t *); int ffs_realloccg(struct inode *, daddr_t, daddr_t, int, int , struct ucred *, struct buf **, daddr_t *); int ffs_reallocblks(void *); int ffs_inode_alloc(struct inode *, mode_t, struct ucred *, struct vnode **); int ffs_inode_free(struct inode *, ino_t, mode_t); int ffs_freefile(struct inode *, ino_t, mode_t); daddr_t ffs_blkpref(struct inode *, daddr_t, int, daddr_t *); void ffs_blkfree(struct inode *, daddr_t, long); void ffs_clusteracct(struct fs *, struct cg *, daddr_t, int); /* ffs_balloc.c */ int ffs_balloc(struct inode *, off_t, int, struct ucred *, int, struct buf **); /* ffs_inode.c */ int ffs_init(struct vfsconf *); int ffs_update(struct inode *, struct timespec *, struct timespec *, int); int ffs_truncate(struct inode *, off_t, int, struct ucred *); /* ffs_subr.c */ int ffs_bufatoff(struct inode *, off_t, char **, struct buf **); void ffs_fragacct(struct fs *, int, int32_t[], int); #ifdef DIAGNOSTIC void ffs_checkoverlap(struct buf *, struct inode *); #endif int ffs_isfreeblock(struct fs *, unsigned char *, daddr_t); int ffs_isblock(struct fs *, unsigned char *, daddr_t); void ffs_clrblock(struct fs *, u_char *, daddr_t); void ffs_setblock(struct fs *, unsigned char *, daddr_t); /* ffs_vfsops.c */ int ffs_mountroot(void); int ffs_mount(struct mount *, const char *, void *, struct nameidata *, struct proc *); int ffs_reload(struct mount *, struct ucred *, struct proc *); int ffs_mountfs(struct vnode *, struct mount *, struct proc *); int ffs_oldfscompat(struct fs *); int ffs_unmount(struct mount *, int, struct proc *); int ffs_flushfiles(struct mount *, int, struct proc *); int ffs_statfs(struct mount *, struct statfs *, struct proc *); int ffs_sync(struct mount *, int, struct ucred *, struct proc *); int ffs_vget(struct mount *, ino_t, struct vnode **); int ffs_fhtovp(struct mount *, struct fid *, struct vnode **); int ffs_vptofh(struct vnode *, struct fid *); int ffs_sysctl(int *, u_int, void *, size_t *, void *, size_t, struct proc *); int ffs_sbupdate(struct ufsmount *, int); int ffs_cgupdate(struct ufsmount *, int); /* ffs_vnops.c */ int ffs_read(void *); int ffs_write(void *); int ffs_fsync(void *); int ffs_reclaim(void *); int ffsfifo_reclaim(void *); /* * Soft dependency function prototypes. */ struct vop_vfree_args; struct vop_fsync_args; void softdep_initialize(void); int softdep_process_worklist(struct mount *); int softdep_mount(struct vnode *, struct mount *, struct fs *, struct ucred *); int softdep_flushworklist(struct mount *, int *, struct proc *); int softdep_flushfiles(struct mount *, int, struct proc *); void softdep_update_inodeblock(struct inode *, struct buf *, int); void softdep_load_inodeblock(struct inode *); void softdep_freefile(struct vnode *, ino_t, mode_t); void softdep_setup_freeblocks(struct inode *, off_t); void softdep_setup_inomapdep(struct buf *, struct inode *, ino_t); void softdep_setup_blkmapdep(struct buf *, struct fs *, daddr_t); void softdep_setup_allocdirect(struct inode *, ufs_lbn_t, daddr_t, daddr_t, long, long, struct buf *); void softdep_setup_allocindir_meta(struct buf *, struct inode *, struct buf *, int, daddr_t); void softdep_setup_allocindir_page(struct inode *, ufs_lbn_t, struct buf *, int, daddr_t, daddr_t, struct buf *); void softdep_fsync_mountdev(struct vnode *); int softdep_sync_metadata(struct vop_fsync_args *); int softdep_fsync(struct vnode *vp); __END_DECLS extern int (**ffs_vnodeop_p)(void *); extern int (**ffs_specop_p)(void *); #ifdef FIFO extern int (**ffs_fifoop_p)(void *); #define FFS_FIFOOPS ffs_fifoop_p #else #define FFS_FIFOOPS NULL #endif extern struct pool ffs_ino_pool; makefs/src/sys/ufs/ffs/ffs_inode.c010064400000000000000000000376461021267303200143470ustar00/* $OpenBSD: ffs_inode.c,v 1.37 2004/06/24 19:35:26 tholo Exp $ */ /* $NetBSD: ffs_inode.c,v 1.10 1996/05/11 18:27:19 mycroft Exp $ */ /* * Copyright (c) 1982, 1986, 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. * * @(#)ffs_inode.c 8.8 (Berkeley) 10/19/94 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int ffs_indirtrunc(struct inode *, daddr_t, daddr_t, daddr_t, int, long *); /* * Update the access, modified, and inode change times as specified by the * IN_ACCESS, IN_UPDATE, and IN_CHANGE flags respectively. The IN_MODIFIED * flag is used to specify that the inode needs to be updated but that the * times have already been set. The access and modified times are taken from * the second and third parameters; the inode change time is always taken * from the current time. If waitfor is set, then wait for the disk write * of the inode to complete. */ int ffs_update(struct inode *ip, struct timespec *atime, struct timespec *mtime, int waitfor) { struct vnode *vp; struct fs *fs; struct buf *bp; int error; struct timespec ts; TIMEVAL_TO_TIMESPEC(&time, &ts); vp = ITOV(ip); if (vp->v_mount->mnt_flag & MNT_RDONLY) { ip->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE); return (0); } else if ((vp->v_mount->mnt_flag & MNT_NOATIME) && !(ip->i_flag & (IN_CHANGE | IN_UPDATE))) { ip->i_flag &= ~IN_ACCESS; } if ((ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0 && waitfor != MNT_WAIT) return (0); if (ip->i_flag & IN_ACCESS) { ip->i_ffs_atime = atime ? atime->tv_sec : ts.tv_sec; ip->i_ffs_atimensec = atime ? atime->tv_nsec : ts.tv_nsec; } if (ip->i_flag & IN_UPDATE) { ip->i_ffs_mtime = mtime ? mtime->tv_sec : ts.tv_sec; ip->i_ffs_mtimensec = mtime ? mtime->tv_nsec : ts.tv_nsec; ip->i_modrev++; } if (ip->i_flag & IN_CHANGE) { ip->i_ffs_ctime = time.tv_sec; ip->i_ffs_ctimensec = time.tv_usec * 1000; } ip->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE); fs = ip->i_fs; /* * Ensure that uid and gid are correct. This is a temporary * fix until fsck has been changed to do the update. */ if (fs->fs_inodefmt < FS_44INODEFMT) { /* XXX */ ip->i_din1.di_ouid = ip->i_ffs_uid; /* XXX */ ip->i_din1.di_ogid = ip->i_ffs_gid; /* XXX */ } /* XXX */ error = bread(ip->i_devvp, fsbtodb(fs, ino_to_fsba(fs, ip->i_number)), (int)fs->fs_bsize, NOCRED, &bp); if (error) { brelse(bp); return (error); } if (DOINGSOFTDEP(vp)) softdep_update_inodeblock(ip, bp, waitfor); else if (ip->i_effnlink != ip->i_ffs_nlink) panic("ffs_update: bad link cnt"); *((struct ufs1_dinode *)bp->b_data + ino_to_fsbo(fs, ip->i_number)) = ip->i_din1; if (waitfor && !DOINGASYNC(vp)) { return (bwrite(bp)); } else { bdwrite(bp); return (0); } } #define SINGLE 0 /* index of single indirect block */ #define DOUBLE 1 /* index of double indirect block */ #define TRIPLE 2 /* index of triple indirect block */ /* * Truncate the inode oip to at most length size, freeing the * disk blocks. */ int ffs_truncate(struct inode *oip, off_t length, int flags, struct ucred *cred) { struct vnode *ovp; daddr_t lastblock; daddr_t bn, lbn, lastiblock[NIADDR], indir_lbn[NIADDR]; daddr_t oldblks[NDADDR + NIADDR], newblks[NDADDR + NIADDR]; struct fs *fs; struct buf *bp; int offset, size, level; long count, nblocks, vflags, blocksreleased = 0; register int i; int aflags, error, allerror; off_t osize; if (length < 0) return (EINVAL); ovp = ITOV(oip); if (ovp->v_type != VREG && ovp->v_type != VDIR && ovp->v_type != VLNK) return (0); if (oip->i_ffs_size == length) return (0); if (ovp->v_type == VLNK && (oip->i_ffs_size < ovp->v_mount->mnt_maxsymlinklen || (ovp->v_mount->mnt_maxsymlinklen == 0 && oip->i_din1.di_blocks == 0))) { #ifdef DIAGNOSTIC if (length != 0) panic("ffs_truncate: partial truncate of symlink"); #endif bzero((char *)&oip->i_ffs_shortlink, (u_int)oip->i_ffs_size); oip->i_ffs_size = 0; oip->i_flag |= IN_CHANGE | IN_UPDATE; return (UFS_UPDATE(oip, MNT_WAIT)); } if ((error = getinoquota(oip)) != 0) return (error); uvm_vnp_setsize(ovp, length); oip->i_ci.ci_lasta = oip->i_ci.ci_clen = oip->i_ci.ci_cstart = oip->i_ci.ci_lastw = 0; if (DOINGSOFTDEP(ovp)) { if (length > 0 || softdep_slowdown(ovp)) { /* * If a file is only partially truncated, then * we have to clean up the data structures * describing the allocation past the truncation * point. Finding and deallocating those structures * is a lot of work. Since partial truncation occurs * rarely, we solve the problem by syncing the file * so that it will have no data structures left. */ if ((error = VOP_FSYNC(ovp, cred, MNT_WAIT, curproc)) != 0) return (error); } else { (void)ufs_quota_free_blocks(oip, oip->i_ffs_blocks, NOCRED); softdep_setup_freeblocks(oip, length); (void) vinvalbuf(ovp, 0, cred, curproc, 0, 0); oip->i_flag |= IN_CHANGE | IN_UPDATE; return (UFS_UPDATE(oip, 0)); } } fs = oip->i_fs; osize = oip->i_ffs_size; /* * Lengthen the size of the file. We must ensure that the * last byte of the file is allocated. Since the smallest * value of osize is 0, length will be at least 1. */ if (osize < length) { if (length > fs->fs_maxfilesize) return (EFBIG); aflags = B_CLRBUF; if (flags & IO_SYNC) aflags |= B_SYNC; error = UFS_BUF_ALLOC(oip, length - 1, 1, cred, aflags, &bp); if (error) return (error); oip->i_ffs_size = length; uvm_vnp_setsize(ovp, length); (void) uvm_vnp_uncache(ovp); if (aflags & B_SYNC) bwrite(bp); else bawrite(bp); oip->i_flag |= IN_CHANGE | IN_UPDATE; return (UFS_UPDATE(oip, MNT_WAIT)); } uvm_vnp_setsize(ovp, length); /* * Shorten the size of the file. If the file is not being * truncated to a block boundary, the contents of the * partial block following the end of the file must be * zero'ed in case it ever becomes accessible again because * of subsequent file growth. Directories however are not * zero'ed as they should grow back initialized to empty. */ offset = blkoff(fs, length); if (offset == 0) { oip->i_ffs_size = length; } else { lbn = lblkno(fs, length); aflags = B_CLRBUF; if (flags & IO_SYNC) aflags |= B_SYNC; error = UFS_BUF_ALLOC(oip, length - 1, 1, cred, aflags, &bp); if (error) return (error); /* * When we are doing soft updates and the UFS_BALLOC * above fills in a direct block hole with a full sized * block that will be truncated down to a fragment below, * we must flush out the block dependency with an FSYNC * so that we do not get a soft updates inconsistency * when we create the fragment below. */ if (DOINGSOFTDEP(ovp) && lbn < NDADDR && fragroundup(fs, blkoff(fs, length)) < fs->fs_bsize && (error = VOP_FSYNC(ovp, cred, MNT_WAIT, curproc)) != 0) return (error); oip->i_ffs_size = length; size = blksize(fs, oip, lbn); (void) uvm_vnp_uncache(ovp); if (ovp->v_type != VDIR) bzero((char *)bp->b_data + offset, (u_int)(size - offset)); allocbuf(bp, size); if (aflags & B_SYNC) bwrite(bp); else bawrite(bp); } /* * Calculate index into inode's block list of * last direct and indirect blocks (if any) * which we want to keep. Lastblock is -1 when * the file is truncated to 0. */ lastblock = lblkno(fs, length + fs->fs_bsize - 1) - 1; lastiblock[SINGLE] = lastblock - NDADDR; lastiblock[DOUBLE] = lastiblock[SINGLE] - NINDIR(fs); lastiblock[TRIPLE] = lastiblock[DOUBLE] - NINDIR(fs) * NINDIR(fs); nblocks = btodb(fs->fs_bsize); /* * Update file and block pointers on disk before we start freeing * blocks. If we crash before free'ing blocks below, the blocks * will be returned to the free list. lastiblock values are also * normalized to -1 for calls to ffs_indirtrunc below. */ bcopy((caddr_t)&oip->i_ffs_db[0], (caddr_t)oldblks, sizeof oldblks); for (level = TRIPLE; level >= SINGLE; level--) if (lastiblock[level] < 0) { oip->i_ffs_ib[level] = 0; lastiblock[level] = -1; } for (i = NDADDR - 1; i > lastblock; i--) oip->i_ffs_db[i] = 0; oip->i_flag |= IN_CHANGE | IN_UPDATE; if ((error = UFS_UPDATE(oip, MNT_WAIT)) != 0) allerror = error; /* * Having written the new inode to disk, save its new configuration * and put back the old block pointers long enough to process them. * Note that we save the new block configuration so we can check it * when we are done. */ bcopy((caddr_t)&oip->i_ffs_db[0], (caddr_t)newblks, sizeof newblks); bcopy((caddr_t)oldblks, (caddr_t)&oip->i_ffs_db[0], sizeof oldblks); oip->i_ffs_size = osize; vflags = ((length > 0) ? V_SAVE : 0) | V_SAVEMETA; allerror = vinvalbuf(ovp, vflags, cred, curproc, 0, 0); /* * Indirect blocks first. */ indir_lbn[SINGLE] = -NDADDR; indir_lbn[DOUBLE] = indir_lbn[SINGLE] - NINDIR(fs) - 1; indir_lbn[TRIPLE] = indir_lbn[DOUBLE] - NINDIR(fs) * NINDIR(fs) - 1; for (level = TRIPLE; level >= SINGLE; level--) { bn = oip->i_ffs_ib[level]; if (bn != 0) { error = ffs_indirtrunc(oip, indir_lbn[level], fsbtodb(fs, bn), lastiblock[level], level, &count); if (error) allerror = error; blocksreleased += count; if (lastiblock[level] < 0) { oip->i_ffs_ib[level] = 0; ffs_blkfree(oip, bn, fs->fs_bsize); blocksreleased += nblocks; } } if (lastiblock[level] >= 0) goto done; } /* * All whole direct blocks or frags. */ for (i = NDADDR - 1; i > lastblock; i--) { register long bsize; bn = oip->i_ffs_db[i]; if (bn == 0) continue; oip->i_ffs_db[i] = 0; bsize = blksize(fs, oip, i); ffs_blkfree(oip, bn, bsize); blocksreleased += btodb(bsize); } if (lastblock < 0) goto done; /* * Finally, look for a change in size of the * last direct block; release any frags. */ bn = oip->i_ffs_db[lastblock]; if (bn != 0) { long oldspace, newspace; /* * Calculate amount of space we're giving * back as old block size minus new block size. */ oldspace = blksize(fs, oip, lastblock); oip->i_ffs_size = length; newspace = blksize(fs, oip, lastblock); if (newspace == 0) panic("ffs_truncate: newspace"); if (oldspace - newspace > 0) { /* * Block number of space to be free'd is * the old block # plus the number of frags * required for the storage we're keeping. */ bn += numfrags(fs, newspace); ffs_blkfree(oip, bn, oldspace - newspace); blocksreleased += btodb(oldspace - newspace); } } done: #ifdef DIAGNOSTIC for (level = SINGLE; level <= TRIPLE; level++) if (newblks[NDADDR + level] != oip->i_ffs_ib[level]) panic("ffs_truncate1"); for (i = 0; i < NDADDR; i++) if (newblks[i] != oip->i_ffs_db[i]) panic("ffs_truncate2"); #endif /* DIAGNOSTIC */ /* * Put back the real size. */ oip->i_ffs_size = length; oip->i_ffs_blocks -= blocksreleased; if (oip->i_ffs_blocks < 0) /* sanity */ oip->i_ffs_blocks = 0; oip->i_flag |= IN_CHANGE; (void)ufs_quota_free_blocks(oip, blocksreleased, NOCRED); return (allerror); } /* * Release blocks associated with the inode ip and stored in the indirect * block bn. Blocks are free'd in LIFO order up to (but not including) * lastbn. If level is greater than SINGLE, the block is an indirect block * and recursive calls to indirtrunc must be used to cleanse other indirect * blocks. * * NB: triple indirect blocks are untested. */ static int ffs_indirtrunc(ip, lbn, dbn, lastbn, level, countp) register struct inode *ip; daddr_t lbn, lastbn; daddr_t dbn; int level; long *countp; { register int i; struct buf *bp; register struct fs *fs = ip->i_fs; register daddr_t *bap; struct vnode *vp; daddr_t *copy = NULL, nb, nlbn, last; long blkcount, factor; int nblocks, blocksreleased = 0; int error = 0, allerror = 0; /* * Calculate index in current block of last * block to be kept. -1 indicates the entire * block so we need not calculate the index. */ factor = 1; for (i = SINGLE; i < level; i++) factor *= NINDIR(fs); last = lastbn; if (lastbn > 0) last /= factor; nblocks = btodb(fs->fs_bsize); /* * Get buffer of block pointers, zero those entries corresponding * to blocks to be free'd, and update on disk copy first. Since * double(triple) indirect before single(double) indirect, calls * to bmap on these blocks will fail. However, we already have * the on disk address, so we have to set the b_blkno field * explicitly instead of letting bread do everything for us. */ vp = ITOV(ip); bp = getblk(vp, lbn, (int)fs->fs_bsize, 0, 0); if (!(bp->b_flags & (B_DONE | B_DELWRI))) { curproc->p_stats->p_ru.ru_inblock++; /* pay for read */ bp->b_flags |= B_READ; if (bp->b_bcount > bp->b_bufsize) panic("ffs_indirtrunc: bad buffer size"); bp->b_blkno = dbn; VOP_STRATEGY(bp); error = biowait(bp); } if (error) { brelse(bp); *countp = 0; return (error); } bap = (daddr_t *)bp->b_data; if (lastbn != -1) { MALLOC(copy, daddr_t *, fs->fs_bsize, M_TEMP, M_WAITOK); bcopy((caddr_t)bap, (caddr_t)copy, (u_int)fs->fs_bsize); bzero((caddr_t)&bap[last + 1], (u_int)(NINDIR(fs) - (last + 1)) * sizeof (daddr_t)); if (!DOINGASYNC(vp)) { error = bwrite(bp); if (error) allerror = error; } else { bawrite(bp); } bap = copy; } /* * Recursively free totally unused blocks. */ for (i = NINDIR(fs) - 1, nlbn = lbn + 1 - i * factor; i > last; i--, nlbn += factor) { nb = bap[i]; if (nb == 0) continue; if (level > SINGLE) { error = ffs_indirtrunc(ip, nlbn, fsbtodb(fs, nb), (daddr_t)-1, level - 1, &blkcount); if (error) allerror = error; blocksreleased += blkcount; } ffs_blkfree(ip, nb, fs->fs_bsize); blocksreleased += nblocks; } /* * Recursively free last partial block. */ if (level > SINGLE && lastbn >= 0) { last = lastbn % factor; nb = bap[i]; if (nb != 0) { error = ffs_indirtrunc(ip, nlbn, fsbtodb(fs, nb), last, level - 1, &blkcount); if (error) allerror = error; blocksreleased += blkcount; } } if (copy != NULL) { FREE(copy, M_TEMP); } else { bp->b_flags |= B_INVAL; brelse(bp); } *countp = blocksreleased; return (allerror); } makefs/src/sys/ufs/ffs/ffs_softdep.c010064400000000000000000004642521314214547200147210ustar00/** $MirOS: src/sys/ufs/ffs/ffs_softdep.c,v 1.8 2017/08/07 20:18:39 tg Exp $ */ /* $OpenBSD: ffs_softdep.c,v 1.60+1.63+1.64+1.69+1.71+1.74+1.77+1.78+1.79+1.102 2005/07/20 16:30:34 pedro Exp $ */ /* * Copyright 1998, 2000 Marshall Kirk McKusick. All Rights Reserved. * * The soft updates code is derived from the appendix of a University * of Michigan technical report (Gregory R. Ganger and Yale N. Patt, * "Soft Updates: A Solution to the Metadata Update Problem in File * Systems", CSE-TR-254-95, August 1995). * * Further information about soft updates can be obtained from: * * Marshall Kirk McKusick http://www.mckusick.com/softdep/ * 1614 Oxford Street mckusick@mckusick.com * Berkeley, CA 94709-1608 +1-510-843-9542 * USA * * 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 MARSHALL KIRK MCKUSICK ``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 MARSHALL KIRK MCKUSICK 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: @(#)ffs_softdep.c 9.59 (McKusick) 6/21/00 * $FreeBSD: src/sys/ufs/ffs/ffs_softdep.c,v 1.86 2001/02/04 16:08:18 phk Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define STATIC /* * Mapping of dependency structure types to malloc types. */ #define D_PAGEDEP 0 #define D_INODEDEP 1 #define D_NEWBLK 2 #define D_BMSAFEMAP 3 #define D_ALLOCDIRECT 4 #define D_INDIRDEP 5 #define D_ALLOCINDIR 6 #define D_FREEFRAG 7 #define D_FREEBLKS 8 #define D_FREEFILE 9 #define D_DIRADD 10 #define D_MKDIR 11 #define D_DIRREM 12 #define D_NEWDIRBLK 13 #define D_LAST 13 /* * Names of softdep types. */ const char *softdep_typenames[] = { "pagedep", "inodedep", "newblk", "bmsafemap", "allocdirect", "indirdep", "allocindir", "freefrag", "freeblks", "freefile", "diradd", "mkdir", "dirrem", "newdirblk", }; #define TYPENAME(type) \ ((unsigned)(type) <= D_LAST ? softdep_typenames[type] : "???") /* * Finding the current process. */ #define CURPROC curproc /* * End system adaptaion definitions. */ /* * Internal function prototypes. */ STATIC void softdep_error(char *, int); STATIC void drain_output(struct vnode *, int); STATIC int getdirtybuf(struct buf **, int); STATIC void clear_remove(struct proc *); STATIC void clear_inodedeps(struct proc *); STATIC int flush_pagedep_deps(struct vnode *, struct mount *, struct diraddhd *); STATIC int flush_inodedep_deps(struct fs *, ino_t); STATIC int handle_written_filepage(struct pagedep *, struct buf *); STATIC void diradd_inode_written(struct diradd *, struct inodedep *); STATIC int handle_written_inodeblock(struct inodedep *, struct buf *); STATIC void handle_allocdirect_partdone(struct allocdirect *); STATIC void handle_allocindir_partdone(struct allocindir *); STATIC void initiate_write_filepage(struct pagedep *, struct buf *); STATIC void handle_written_mkdir(struct mkdir *, int); STATIC void initiate_write_inodeblock(struct inodedep *, struct buf *); STATIC void handle_workitem_freefile(struct freefile *); STATIC void handle_workitem_remove(struct dirrem *); STATIC struct dirrem *newdirrem(struct buf *, struct inode *, struct inode *, int, struct dirrem **); STATIC void free_diradd(struct diradd *); STATIC void free_allocindir(struct allocindir *, struct inodedep *); STATIC void free_newdirblk(struct newdirblk *); STATIC int indir_trunc(struct inode *, daddr_t, int, ufs_lbn_t, long *); STATIC void deallocate_dependencies(struct buf *, struct inodedep *); STATIC void free_allocdirect(struct allocdirectlst *, struct allocdirect *, int); STATIC int check_inode_unwritten(struct inodedep *); STATIC int free_inodedep(struct inodedep *); STATIC void handle_workitem_freeblocks(struct freeblks *); STATIC void merge_inode_lists(struct inodedep *); STATIC void setup_allocindir_phase2(struct buf *, struct inode *, struct allocindir *); STATIC struct allocindir *newallocindir(struct inode *, int, daddr_t, daddr_t); STATIC void handle_workitem_freefrag(struct freefrag *); STATIC struct freefrag *newfreefrag(struct inode *, daddr_t, long); STATIC void allocdirect_merge(struct allocdirectlst *, struct allocdirect *, struct allocdirect *); STATIC struct bmsafemap *bmsafemap_lookup(struct buf *); STATIC int newblk_lookup(struct fs *, daddr_t, int, struct newblk **); STATIC int inodedep_lookup(struct fs *, ino_t, int, struct inodedep **); STATIC int pagedep_lookup(struct inode *, ufs_lbn_t, int, struct pagedep **); STATIC void pause_timer(void *); STATIC int request_cleanup(int, int); STATIC int process_worklist_item(struct mount *, int); STATIC void add_to_worklist(struct worklist *); /* * Exported softdep operations. */ void softdep_disk_io_initiation(struct buf *); void softdep_disk_write_complete(struct buf *); void softdep_deallocate_dependencies(struct buf *); void softdep_move_dependencies(struct buf *, struct buf *); int softdep_count_dependencies(struct buf *bp, int, int); /* * Locking primitives. * * For a uniprocessor, all we need to do is protect against disk * interrupts. For a multiprocessor, this lock would have to be * a mutex. A single mutex is used throughout this file, though * finer grain locking could be used if contention warranted it. * * For a multiprocessor, the sleep call would accept a lock and * release it after the sleep processing was complete. In a uniprocessor * implementation there is no such interlock, so we simple mark * the places where it needs to be done with the `interlocked' form * of the lock calls. Since the uniprocessor sleep already interlocks * the spl, there is nothing that really needs to be done. */ #ifndef /* NOT */ DEBUG STATIC struct lockit { int lkt_spl; } lk = { 0 }; #define ACQUIRE_LOCK(lk) (lk)->lkt_spl = splbio() #define FREE_LOCK(lk) splx((lk)->lkt_spl) #define ACQUIRE_LOCK_INTERLOCKED(lk,s) (lk)->lkt_spl = (s) #define FREE_LOCK_INTERLOCKED(lk) ((lk)->lkt_spl) #else /* DEBUG */ STATIC struct lockit { int lkt_spl; pid_t lkt_held; int lkt_line; } lk = { 0, -1 }; STATIC int lockcnt; STATIC void acquire_lock(struct lockit *, int); STATIC void free_lock(struct lockit *, int); STATIC void acquire_lock_interlocked(struct lockit *, int, int); STATIC int free_lock_interlocked(struct lockit *, int); #define ACQUIRE_LOCK(lk) acquire_lock(lk, __LINE__) #define FREE_LOCK(lk) free_lock(lk, __LINE__) #define ACQUIRE_LOCK_INTERLOCKED(lk,s) acquire_lock_interlocked(lk, (s), __LINE__) #define FREE_LOCK_INTERLOCKED(lk) free_lock_interlocked(lk, __LINE__) STATIC void acquire_lock(lk, line) struct lockit *lk; int line; { pid_t holder; int original_line; if (lk->lkt_held != -1) { holder = lk->lkt_held; original_line = lk->lkt_line; FREE_LOCK(lk); if (holder == CURPROC->p_pid) panic("softdep_lock: locking against myself, acquired at line %d, relocked at line %d", original_line, line); else panic("softdep_lock: lock held by %d, acquired at line %d, relocked at line %d", holder, original_line, line); } lk->lkt_spl = splbio(); lk->lkt_held = CURPROC->p_pid; lk->lkt_line = line; lockcnt++; } STATIC void free_lock(lk, line) struct lockit *lk; int line; { if (lk->lkt_held == -1) panic("softdep_unlock: lock not held at line %d", line); lk->lkt_held = -1; splx(lk->lkt_spl); } STATIC void acquire_lock_interlocked(lk, s, line) struct lockit *lk; int s; int line; { pid_t holder; int original_line; if (lk->lkt_held != -1) { holder = lk->lkt_held; original_line = lk->lkt_line; FREE_LOCK_INTERLOCKED(lk); if (holder == CURPROC->p_pid) panic("softdep_lock: locking against myself, acquired at line %d, relocked at line %d", original_line, line); else panic("softdep_lock: lock held by %d, acquired at line %d, relocked at line %d", holder, original_line, line); } lk->lkt_held = CURPROC->p_pid; lk->lkt_line = line; lk->lkt_spl = s; lockcnt++; } STATIC int free_lock_interlocked(lk, line) struct lockit *lk; int line; { if (lk->lkt_held == -1) panic("softdep_unlock_interlocked: lock not held at line %d", line); lk->lkt_held = -1; return (lk->lkt_spl); } #endif /* DEBUG */ /* * Place holder for real semaphores. */ struct sema { int value; pid_t holder; char *name; int prio; int timo; }; STATIC void sema_init(struct sema *, char *, int, int); STATIC int sema_get(struct sema *, struct lockit *); STATIC void sema_release(struct sema *); STATIC void sema_init(semap, name, prio, timo) struct sema *semap; char *name; int prio, timo; { semap->holder = -1; semap->value = 0; semap->name = name; semap->prio = prio; semap->timo = timo; } STATIC int sema_get(semap, interlock) struct sema *semap; struct lockit *interlock; { int s = 0; if (semap->value++ > 0) { if (interlock != NULL) s = FREE_LOCK_INTERLOCKED(interlock); tsleep((caddr_t)semap, semap->prio, semap->name, semap->timo); if (interlock != NULL) { ACQUIRE_LOCK_INTERLOCKED(interlock, s); FREE_LOCK(interlock); } return (0); } semap->holder = CURPROC->p_pid; if (interlock != NULL) FREE_LOCK(interlock); return (1); } STATIC void sema_release(semap) struct sema *semap; { if (semap->value <= 0 || semap->holder != CURPROC->p_pid) { #ifdef DEBUG if (lk.lkt_held != -1) FREE_LOCK(&lk); #endif panic("sema_release: not held"); } if (--semap->value > 0) { semap->value = 0; wakeup(semap); } semap->holder = -1; } /* * Memory management. */ STATIC struct pool pagedep_pool; STATIC struct pool inodedep_pool; STATIC struct pool newblk_pool; STATIC struct pool bmsafemap_pool; STATIC struct pool allocdirect_pool; STATIC struct pool indirdep_pool; STATIC struct pool allocindir_pool; STATIC struct pool freefrag_pool; STATIC struct pool freeblks_pool; STATIC struct pool freefile_pool; STATIC struct pool diradd_pool; STATIC struct pool mkdir_pool; STATIC struct pool dirrem_pool; STATIC struct pool newdirblk_pool; static __inline void softdep_free(struct worklist *item, int type) { switch (type) { case D_PAGEDEP: pool_put(&pagedep_pool, item); break; case D_INODEDEP: pool_put(&inodedep_pool, item); break; case D_BMSAFEMAP: pool_put(&bmsafemap_pool, item); break; case D_ALLOCDIRECT: pool_put(&allocdirect_pool, item); break; case D_INDIRDEP: pool_put(&indirdep_pool, item); break; case D_ALLOCINDIR: pool_put(&allocindir_pool, item); break; case D_FREEFRAG: pool_put(&freefrag_pool, item); break; case D_FREEBLKS: pool_put(&freeblks_pool, item); break; case D_FREEFILE: pool_put(&freefile_pool, item); break; case D_DIRADD: pool_put(&diradd_pool, item); break; case D_MKDIR: pool_put(&mkdir_pool, item); break; case D_DIRREM: pool_put(&dirrem_pool, item); break; case D_NEWDIRBLK: pool_put(&newdirblk_pool, item); break; default: #ifdef DEBUG if (lk.lkt_held != -1) FREE_LOCK(&lk); #endif panic("softdep_free: unknown type %d", type); } } struct workhead softdep_freequeue; static __inline void softdep_freequeue_add(struct worklist *item) { int s; s = splbio(); LIST_INSERT_HEAD(&softdep_freequeue, item, wk_list); splx(s); } static __inline void softdep_freequeue_process(void) { struct worklist *wk; while ((wk = LIST_FIRST(&softdep_freequeue)) != NULL) { LIST_REMOVE(wk, wk_list); FREE_LOCK(&lk); softdep_free(wk, wk->wk_type); ACQUIRE_LOCK(&lk); } } /* * Worklist queue management. * These routines require that the lock be held. */ #ifndef /* NOT */ DEBUG #define WORKLIST_INSERT(head, item) do { \ (item)->wk_state |= ONWORKLIST; \ LIST_INSERT_HEAD(head, item, wk_list); \ } while (0) #define WORKLIST_REMOVE(item) do { \ (item)->wk_state &= ~ONWORKLIST; \ LIST_REMOVE(item, wk_list); \ } while (0) #define WORKITEM_FREE(item, type) softdep_freequeue_add((struct worklist *)item) #else /* DEBUG */ STATIC void worklist_insert(struct workhead *, struct worklist *); STATIC void worklist_remove(struct worklist *); STATIC void workitem_free(struct worklist *); #define WORKLIST_INSERT(head, item) worklist_insert(head, item) #define WORKLIST_REMOVE(item) worklist_remove(item) #define WORKITEM_FREE(item, type) workitem_free((struct worklist *)item) STATIC void worklist_insert(head, item) struct workhead *head; struct worklist *item; { if (lk.lkt_held == -1) panic("worklist_insert: lock not held"); if (item->wk_state & ONWORKLIST) { FREE_LOCK(&lk); panic("worklist_insert: already on list"); } item->wk_state |= ONWORKLIST; LIST_INSERT_HEAD(head, item, wk_list); } STATIC void worklist_remove(item) struct worklist *item; { if (lk.lkt_held == -1) panic("worklist_remove: lock not held"); if ((item->wk_state & ONWORKLIST) == 0) { FREE_LOCK(&lk); panic("worklist_remove: not on list"); } item->wk_state &= ~ONWORKLIST; LIST_REMOVE(item, wk_list); } STATIC void workitem_free(item) struct worklist *item; { if (item->wk_state & ONWORKLIST) { if (lk.lkt_held != -1) FREE_LOCK(&lk); panic("workitem_free: still on list"); } softdep_freequeue_add(item); } #endif /* DEBUG */ /* * Workitem queue management */ STATIC struct workhead softdep_workitem_pending; STATIC struct worklist *worklist_tail; STATIC int num_on_worklist; /* number of worklist items to be processed */ STATIC int softdep_worklist_busy; /* 1 => trying to do unmount */ STATIC int softdep_worklist_req; /* serialized waiters */ STATIC int max_softdeps; /* maximum number of structs before slowdown */ STATIC int tickdelay = 2; /* number of ticks to pause during slowdown */ STATIC int proc_waiting; /* tracks whether we have a timeout posted */ STATIC int *stat_countp; /* statistic to count in proc_waiting timeout */ STATIC struct timeout proc_waiting_timeout; STATIC struct proc *filesys_syncer; /* proc of filesystem syncer process */ STATIC int req_clear_inodedeps; /* syncer process flush some inodedeps */ #define FLUSH_INODES 1 STATIC int req_clear_remove; /* syncer process flush some freeblks */ #define FLUSH_REMOVE 2 /* * runtime statistics */ STATIC int stat_worklist_push; /* number of worklist cleanups */ STATIC int stat_blk_limit_push; /* number of times block limit neared */ STATIC int stat_ino_limit_push; /* number of times inode limit neared */ STATIC int stat_blk_limit_hit; /* number of times block slowdown imposed */ STATIC int stat_ino_limit_hit; /* number of times inode slowdown imposed */ STATIC int stat_sync_limit_hit; /* number of synchronous slowdowns imposed */ STATIC int stat_indir_blk_ptrs; /* bufs redirtied as indir ptrs not written */ STATIC int stat_inode_bitmap; /* bufs redirtied as inode bitmap not written */ STATIC int stat_direct_blk_ptrs;/* bufs redirtied as direct ptrs not written */ STATIC int stat_dir_entry; /* bufs redirtied as dir entry cannot write */ /* * Add an item to the end of the work queue. * This routine requires that the lock be held. * This is the only routine that adds items to the list. * The following routine is the only one that removes items * and does so in order from first to last. */ STATIC void add_to_worklist(wk) struct worklist *wk; { if (wk->wk_state & ONWORKLIST) { #ifdef DEBUG if (lk.lkt_held != -1) FREE_LOCK(&lk); #endif panic("add_to_worklist: already on list"); } wk->wk_state |= ONWORKLIST; if (LIST_FIRST(&softdep_workitem_pending) == NULL) LIST_INSERT_HEAD(&softdep_workitem_pending, wk, wk_list); else LIST_INSERT_AFTER(worklist_tail, wk, wk_list); worklist_tail = wk; num_on_worklist += 1; } /* * Process that runs once per second to handle items in the background queue. * * Note that we ensure that everything is done in the order in which they * appear in the queue. The code below depends on this property to ensure * that blocks of a file are freed before the inode itself is freed. This * ordering ensures that no new triples will be generated * until all the old ones have been purged from the dependency lists. */ int softdep_process_worklist(matchmnt) struct mount *matchmnt; { struct proc *p = CURPROC; int matchcnt, loopcount; struct timeval starttime; /* * First process any items on the delayed-free queue. */ ACQUIRE_LOCK(&lk); softdep_freequeue_process(); FREE_LOCK(&lk); /* * Record the process identifier of our caller so that we can give * this process preferential treatment in request_cleanup below. * We can't do this in softdep_initialize, because the syncer doesn't * have to run then. * NOTE! This function _could_ be called with a curproc != syncerproc. */ filesys_syncer = syncerproc; matchcnt = 0; /* * There is no danger of having multiple processes run this * code, but we have to single-thread it when softdep_flushfiles() * is in operation to get an accurate count of the number of items * related to its mount point that are in the list. */ if (matchmnt == NULL) { if (softdep_worklist_busy < 0) return(-1); softdep_worklist_busy += 1; } /* * If requested, try removing inode or removal dependencies. */ if (req_clear_inodedeps) { clear_inodedeps(p); req_clear_inodedeps -= 1; wakeup_one(&proc_waiting); } if (req_clear_remove) { clear_remove(p); req_clear_remove -= 1; wakeup_one(&proc_waiting); } loopcount = 1; starttime = time; while (num_on_worklist > 0) { matchcnt += process_worklist_item(matchmnt, 0); /* * If a umount operation wants to run the worklist * accurately, abort. */ if (softdep_worklist_req && matchmnt == NULL) { matchcnt = -1; break; } /* * If requested, try removing inode or removal dependencies. */ if (req_clear_inodedeps) { clear_inodedeps(p); req_clear_inodedeps -= 1; wakeup_one(&proc_waiting); } if (req_clear_remove) { clear_remove(p); req_clear_remove -= 1; wakeup_one(&proc_waiting); } /* * We do not generally want to stop for buffer space, but if * we are really being a buffer hog, we will stop and wait. */ #if 0 if (loopcount++ % 128 == 0) bwillwrite(); #endif /* * Never allow processing to run for more than one * second. Otherwise the other syncer tasks may get * excessively backlogged. */ { struct timeval diff; timersub(&time, &starttime, &diff); if (diff.tv_sec != 0 && matchmnt == NULL) { matchcnt = -1; break; } } /* * Process any new items on the delayed-free queue. */ ACQUIRE_LOCK(&lk); softdep_freequeue_process(); FREE_LOCK(&lk); } if (matchmnt == NULL) { softdep_worklist_busy -= 1; if (softdep_worklist_req && softdep_worklist_busy == 0) wakeup(&softdep_worklist_req); } return (matchcnt); } /* * Process one item on the worklist. */ STATIC int process_worklist_item(matchmnt, flags) struct mount *matchmnt; int flags; { struct worklist *wk, *wkend; struct dirrem *dirrem; struct mount *mp; struct vnode *vp; int matchcnt = 0; ACQUIRE_LOCK(&lk); /* * Normally we just process each item on the worklist in order. * However, if we are in a situation where we cannot lock any * inodes, we have to skip over any dirrem requests whose * vnodes are resident and locked. */ LIST_FOREACH(wk, &softdep_workitem_pending, wk_list) { if ((flags & LK_NOWAIT) == 0 || wk->wk_type != D_DIRREM) break; dirrem = WK_DIRREM(wk); vp = ufs_ihashlookup(VFSTOUFS(dirrem->dm_mnt)->um_dev, dirrem->dm_oldinum); if (vp == NULL || !VOP_ISLOCKED(vp)) break; } if (wk == 0) { FREE_LOCK(&lk); return (0); } /* * Remove the item to be processed. If we are removing the last * item on the list, we need to recalculate the tail pointer. * As this happens rarely and usually when the list is short, * we just run down the list to find it rather than tracking it * in the above loop. */ WORKLIST_REMOVE(wk); if (wk == worklist_tail) { LIST_FOREACH(wkend, &softdep_workitem_pending, wk_list) if (LIST_NEXT(wkend, wk_list) == NULL) break; worklist_tail = wkend; } num_on_worklist -= 1; FREE_LOCK(&lk); switch (wk->wk_type) { case D_DIRREM: /* removal of a directory entry */ mp = WK_DIRREM(wk)->dm_mnt; #if 0 if (vn_write_suspend_wait(NULL, mp, V_NOWAIT)) panic("%s: dirrem on suspended filesystem", "process_worklist_item"); #endif if (mp == matchmnt) matchcnt += 1; handle_workitem_remove(WK_DIRREM(wk)); break; case D_FREEBLKS: /* releasing blocks and/or fragments from a file */ mp = WK_FREEBLKS(wk)->fb_mnt; #if 0 if (vn_write_suspend_wait(NULL, mp, V_NOWAIT)) panic("%s: freeblks on suspended filesystem", "process_worklist_item"); #endif if (mp == matchmnt) matchcnt += 1; handle_workitem_freeblocks(WK_FREEBLKS(wk)); break; case D_FREEFRAG: /* releasing a fragment when replaced as a file grows */ mp = WK_FREEFRAG(wk)->ff_mnt; #if 0 if (vn_write_suspend_wait(NULL, mp, V_NOWAIT)) panic("%s: freefrag on suspended filesystem", "process_worklist_item"); #endif if (mp == matchmnt) matchcnt += 1; handle_workitem_freefrag(WK_FREEFRAG(wk)); break; case D_FREEFILE: /* releasing an inode when its link count drops to 0 */ mp = WK_FREEFILE(wk)->fx_mnt; #if 0 if (vn_write_suspend_wait(NULL, mp, V_NOWAIT)) panic("%s: freefile on suspended filesystem", "process_worklist_item"); #endif if (mp == matchmnt) matchcnt += 1; handle_workitem_freefile(WK_FREEFILE(wk)); break; default: panic("%s_process_worklist: Unknown type %s", "softdep", TYPENAME(wk->wk_type)); /* NOTREACHED */ } return (matchcnt); } /* * Move dependencies from one buffer to another. */ void softdep_move_dependencies(oldbp, newbp) struct buf *oldbp; struct buf *newbp; { struct worklist *wk, *wktail; if (LIST_FIRST(&newbp->b_dep) != NULL) panic("softdep_move_dependencies: need merge code"); wktail = 0; ACQUIRE_LOCK(&lk); while ((wk = LIST_FIRST(&oldbp->b_dep)) != NULL) { LIST_REMOVE(wk, wk_list); if (wktail == 0) LIST_INSERT_HEAD(&newbp->b_dep, wk, wk_list); else LIST_INSERT_AFTER(wktail, wk, wk_list); wktail = wk; } FREE_LOCK(&lk); } /* * Purge the work list of all items associated with a particular mount point. */ int softdep_flushworklist(oldmnt, countp, p) struct mount *oldmnt; int *countp; struct proc *p; { struct vnode *devvp; int count, error = 0; /* * Await our turn to clear out the queue, then serialize access. */ while (softdep_worklist_busy) { softdep_worklist_req += 1; tsleep(&softdep_worklist_req, PRIBIO, "softflush", 0); softdep_worklist_req -= 1; } softdep_worklist_busy = -1; /* * Alternately flush the block device associated with the mount * point and process any dependencies that the flushing * creates. We continue until no more worklist dependencies * are found. */ *countp = 0; devvp = VFSTOUFS(oldmnt)->um_devvp; while ((count = softdep_process_worklist(oldmnt)) > 0) { *countp += count; vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p); error = VOP_FSYNC(devvp, p->p_ucred, MNT_WAIT, p); VOP_UNLOCK(devvp, 0, p); if (error) break; } softdep_worklist_busy = 0; if (softdep_worklist_req) wakeup(&softdep_worklist_req); return (error); } /* * Flush all vnodes and worklist items associated with a specified mount point. */ int softdep_flushfiles(oldmnt, flags, p) struct mount *oldmnt; int flags; struct proc *p; { int error, count, loopcnt; /* * Alternately flush the vnodes associated with the mount * point and process any dependencies that the flushing * creates. In theory, this loop can happen at most twice, * but we give it a few extra just to be sure. */ for (loopcnt = 10; loopcnt > 0; loopcnt--) { /* * Do another flush in case any vnodes were brought in * as part of the cleanup operations. */ if ((error = ffs_flushfiles(oldmnt, flags, p)) != 0) break; if ((error = softdep_flushworklist(oldmnt, &count, p)) != 0 || count == 0) break; } /* * If we are unmounting then it is an error to fail. If we * are simply trying to downgrade to read-only, then filesystem * activity can keep us busy forever, so we just fail with EBUSY. */ if (loopcnt == 0) { error = EBUSY; } return (error); } /* * Structure hashing. * * There are three types of structures that can be looked up: * 1) pagedep structures identified by mount point, inode number, * and logical block. * 2) inodedep structures identified by mount point and inode number. * 3) newblk structures identified by mount point and * physical block number. * * The "pagedep" and "inodedep" dependency structures are hashed * separately from the file blocks and inodes to which they correspond. * This separation helps when the in-memory copy of an inode or * file block must be replaced. It also obviates the need to access * an inode or file page when simply updating (or de-allocating) * dependency structures. Lookup of newblk structures is needed to * find newly allocated blocks when trying to associate them with * their allocdirect or allocindir structure. * * The lookup routines optionally create and hash a new instance when * an existing entry is not found. */ #define DEPALLOC 0x0001 /* allocate structure if lookup fails */ #define NODELAY 0x0002 /* cannot do background work */ /* * Structures and routines associated with pagedep caching. */ LIST_HEAD(pagedep_hashhead, pagedep) *pagedep_hashtbl; u_long pagedep_hash; /* size of hash table - 1 */ #define PAGEDEP_HASH(mp, inum, lbn) \ (&pagedep_hashtbl[((((register_t)(mp)) >> 13) + (inum) + (lbn)) & \ pagedep_hash]) STATIC struct sema pagedep_in_progress; /* * Look up a pagedep. Return 1 if found, 0 if not found or found * when asked to allocate but not associated with any buffer. * If not found, allocate if DEPALLOC flag is passed. * Found or allocated entry is returned in pagedeppp. * This routine must be called with splbio interrupts blocked. */ STATIC int pagedep_lookup(ip, lbn, flags, pagedeppp) struct inode *ip; ufs_lbn_t lbn; int flags; struct pagedep **pagedeppp; { struct pagedep *pagedep; struct pagedep_hashhead *pagedephd; struct mount *mp; int i; #ifdef DEBUG if (lk.lkt_held == -1) panic("pagedep_lookup: lock not held"); #endif mp = ITOV(ip)->v_mount; pagedephd = PAGEDEP_HASH(mp, ip->i_number, lbn); top: LIST_FOREACH(pagedep, pagedephd, pd_hash) if (ip->i_number == pagedep->pd_ino && lbn == pagedep->pd_lbn && mp == pagedep->pd_mnt) break; if (pagedep) { *pagedeppp = pagedep; if ((flags & DEPALLOC) != 0 && (pagedep->pd_state & ONWORKLIST) == 0) return (0); return (1); } if ((flags & DEPALLOC) == 0) { *pagedeppp = NULL; return (0); } if (sema_get(&pagedep_in_progress, &lk) == 0) { ACQUIRE_LOCK(&lk); goto top; } pagedep = pool_get(&pagedep_pool, PR_WAITOK); bzero(pagedep, sizeof(struct pagedep)); pagedep->pd_list.wk_type = D_PAGEDEP; pagedep->pd_mnt = mp; pagedep->pd_ino = ip->i_number; pagedep->pd_lbn = lbn; LIST_INIT(&pagedep->pd_dirremhd); LIST_INIT(&pagedep->pd_pendinghd); for (i = 0; i < DAHASHSZ; i++) LIST_INIT(&pagedep->pd_diraddhd[i]); ACQUIRE_LOCK(&lk); LIST_INSERT_HEAD(pagedephd, pagedep, pd_hash); sema_release(&pagedep_in_progress); *pagedeppp = pagedep; return (0); } /* * Structures and routines associated with inodedep caching. */ LIST_HEAD(inodedep_hashhead, inodedep) *inodedep_hashtbl; STATIC u_long inodedep_hash; /* size of hash table - 1 */ STATIC long num_inodedep; /* number of inodedep allocated */ #define INODEDEP_HASH(fs, inum) \ (&inodedep_hashtbl[((((register_t)(fs)) >> 13) + (inum)) & inodedep_hash]) STATIC struct sema inodedep_in_progress; /* * Look up a inodedep. Return 1 if found, 0 if not found. * If not found, allocate if DEPALLOC flag is passed. * Found or allocated entry is returned in inodedeppp. * This routine must be called with splbio interrupts blocked. */ STATIC int inodedep_lookup(fs, inum, flags, inodedeppp) struct fs *fs; ino_t inum; int flags; struct inodedep **inodedeppp; { struct inodedep *inodedep; struct inodedep_hashhead *inodedephd; int firsttry; #ifdef DEBUG if (lk.lkt_held == -1) panic("inodedep_lookup: lock not held"); #endif firsttry = 1; inodedephd = INODEDEP_HASH(fs, inum); top: LIST_FOREACH(inodedep, inodedephd, id_hash) if (inum == inodedep->id_ino && fs == inodedep->id_fs) break; if (inodedep) { *inodedeppp = inodedep; return (1); } if ((flags & DEPALLOC) == 0) { *inodedeppp = NULL; return (0); } /* * If we are over our limit, try to improve the situation. */ if (num_inodedep > max_softdeps && firsttry && (flags & NODELAY) == 0 && request_cleanup(FLUSH_INODES, 1)) { firsttry = 0; goto top; } if (sema_get(&inodedep_in_progress, &lk) == 0) { ACQUIRE_LOCK(&lk); goto top; } num_inodedep += 1; inodedep = pool_get(&inodedep_pool, PR_WAITOK); inodedep->id_list.wk_type = D_INODEDEP; inodedep->id_fs = fs; inodedep->id_ino = inum; inodedep->id_state = ALLCOMPLETE; inodedep->id_nlinkdelta = 0; inodedep->id_savedino = NULL; inodedep->id_savedsize = -1; inodedep->id_buf = NULL; LIST_INIT(&inodedep->id_pendinghd); LIST_INIT(&inodedep->id_inowait); LIST_INIT(&inodedep->id_bufwait); TAILQ_INIT(&inodedep->id_inoupdt); TAILQ_INIT(&inodedep->id_newinoupdt); ACQUIRE_LOCK(&lk); LIST_INSERT_HEAD(inodedephd, inodedep, id_hash); sema_release(&inodedep_in_progress); *inodedeppp = inodedep; return (0); } /* * Structures and routines associated with newblk caching. */ LIST_HEAD(newblk_hashhead, newblk) *newblk_hashtbl; u_long newblk_hash; /* size of hash table - 1 */ #define NEWBLK_HASH(fs, inum) \ (&newblk_hashtbl[((((register_t)(fs)) >> 13) + (inum)) & newblk_hash]) STATIC struct sema newblk_in_progress; /* * Look up a newblk. Return 1 if found, 0 if not found. * If not found, allocate if DEPALLOC flag is passed. * Found or allocated entry is returned in newblkpp. */ STATIC int newblk_lookup(fs, newblkno, flags, newblkpp) struct fs *fs; daddr_t newblkno; int flags; struct newblk **newblkpp; { struct newblk *newblk; struct newblk_hashhead *newblkhd; newblkhd = NEWBLK_HASH(fs, newblkno); top: LIST_FOREACH(newblk, newblkhd, nb_hash) if (newblkno == newblk->nb_newblkno && fs == newblk->nb_fs) break; if (newblk) { *newblkpp = newblk; return (1); } if ((flags & DEPALLOC) == 0) { *newblkpp = NULL; return (0); } if (sema_get(&newblk_in_progress, 0) == 0) goto top; newblk = pool_get(&newblk_pool, PR_WAITOK); newblk->nb_state = 0; newblk->nb_fs = fs; newblk->nb_newblkno = newblkno; LIST_INSERT_HEAD(newblkhd, newblk, nb_hash); sema_release(&newblk_in_progress); *newblkpp = newblk; return (0); } /* * Executed during filesystem system initialization before * mounting any filesystems. */ void softdep_initialize() { bioops.io_start = softdep_disk_io_initiation; bioops.io_complete = softdep_disk_write_complete; bioops.io_deallocate = softdep_deallocate_dependencies; bioops.io_movedeps = softdep_move_dependencies; bioops.io_countdeps = softdep_count_dependencies; LIST_INIT(&mkdirlisthd); LIST_INIT(&softdep_workitem_pending); #ifdef KMEMSTATS max_softdeps = min (desiredvnodes * 8, kmemstats[M_INODEDEP].ks_limit / (2 * sizeof(struct inodedep))); #else max_softdeps = desiredvnodes * 4; #endif pagedep_hashtbl = hashinit(desiredvnodes / 5, M_PAGEDEP, M_WAITOK, &pagedep_hash); sema_init(&pagedep_in_progress, "pagedep", PRIBIO, 0); inodedep_hashtbl = hashinit(desiredvnodes, M_INODEDEP, M_WAITOK, &inodedep_hash); sema_init(&inodedep_in_progress, "inodedep", PRIBIO, 0); newblk_hashtbl = hashinit(64, M_NEWBLK, M_WAITOK, &newblk_hash); sema_init(&newblk_in_progress, "newblk", PRIBIO, 0); timeout_set(&proc_waiting_timeout, pause_timer, 0); pool_init(&pagedep_pool, sizeof(struct pagedep), 0, 0, 0, "pagedeppl", &pool_allocator_nointr); pool_init(&inodedep_pool, sizeof(struct inodedep), 0, 0, 0, "inodedeppl", &pool_allocator_nointr); pool_init(&newblk_pool, sizeof(struct newblk), 0, 0, 0, "newblkpl", &pool_allocator_nointr); pool_init(&bmsafemap_pool, sizeof(struct bmsafemap), 0, 0, 0, "bmsafemappl", &pool_allocator_nointr); pool_init(&allocdirect_pool, sizeof(struct allocdirect), 0, 0, 0, "allocdirectpl", &pool_allocator_nointr); pool_init(&indirdep_pool, sizeof(struct indirdep), 0, 0, 0, "indirdeppl", &pool_allocator_nointr); pool_init(&allocindir_pool, sizeof(struct allocindir), 0, 0, 0, "allocindirpl", &pool_allocator_nointr); pool_init(&freefrag_pool, sizeof(struct freefrag), 0, 0, 0, "freefragpl", &pool_allocator_nointr); pool_init(&freeblks_pool, sizeof(struct freeblks), 0, 0, 0, "freeblkspl", &pool_allocator_nointr); pool_init(&freefile_pool, sizeof(struct freefile), 0, 0, 0, "freefilepl", &pool_allocator_nointr); pool_init(&diradd_pool, sizeof(struct diradd), 0, 0, 0, "diraddpl", &pool_allocator_nointr); pool_init(&mkdir_pool, sizeof(struct mkdir), 0, 0, 0, "mkdirpl", &pool_allocator_nointr); pool_init(&dirrem_pool, sizeof(struct dirrem), 0, 0, 0, "dirrempl", &pool_allocator_nointr); pool_init(&newdirblk_pool, sizeof(struct newdirblk), 0, 0, 0, "newdirblkpl", &pool_allocator_nointr); } /* * Called at mount time to notify the dependency code that a * filesystem wishes to use it. */ int softdep_mount(devvp, mp, fs, cred) struct vnode *devvp; struct mount *mp; struct fs *fs; struct ucred *cred; { struct csum cstotal; struct cg *cgp; struct buf *bp; int error, cyl; /* * When doing soft updates, the counters in the * superblock may have gotten out of sync, so we have * to scan the cylinder groups and recalculate them. */ if ((fs->fs_flags & FS_UNCLEAN) == 0) return (0); bzero(&cstotal, sizeof cstotal); for (cyl = 0; cyl < fs->fs_ncg; cyl++) { if ((error = bread(devvp, fsbtodb(fs, cgtod(fs, cyl)), fs->fs_cgsize, cred, &bp)) != 0) { brelse(bp); return (error); } cgp = (struct cg *)bp->b_data; cstotal.cs_nffree += cgp->cg_cs.cs_nffree; cstotal.cs_nbfree += cgp->cg_cs.cs_nbfree; cstotal.cs_nifree += cgp->cg_cs.cs_nifree; cstotal.cs_ndir += cgp->cg_cs.cs_ndir; fs->fs_cs(fs, cyl) = cgp->cg_cs; brelse(bp); } #ifdef DEBUG if (bcmp(&cstotal, &fs->fs_cstotal, sizeof cstotal)) printf("ffs_mountfs: superblock updated for soft updates\n"); #endif bcopy(&cstotal, &fs->fs_cstotal, sizeof cstotal); return (0); } /* * Protecting the freemaps (or bitmaps). * * To eliminate the need to execute fsck before mounting a filesystem * after a power failure, one must (conservatively) guarantee that the * on-disk copy of the bitmaps never indicate that a live inode or block is * free. So, when a block or inode is allocated, the bitmap should be * updated (on disk) before any new pointers. When a block or inode is * freed, the bitmap should not be updated until all pointers have been * reset. The latter dependency is handled by the delayed de-allocation * approach described below for block and inode de-allocation. The former * dependency is handled by calling the following procedure when a block or * inode is allocated. When an inode is allocated an "inodedep" is created * with its DEPCOMPLETE flag cleared until its bitmap is written to disk. * Each "inodedep" is also inserted into the hash indexing structure so * that any additional link additions can be made dependent on the inode * allocation. * * The ufs filesystem maintains a number of free block counts (e.g., per * cylinder group, per cylinder and per pair) * in addition to the bitmaps. These counts are used to improve efficiency * during allocation and therefore must be consistent with the bitmaps. * There is no convenient way to guarantee post-crash consistency of these * counts with simple update ordering, for two main reasons: (1) The counts * and bitmaps for a single cylinder group block are not in the same disk * sector. If a disk write is interrupted (e.g., by power failure), one may * be written and the other not. (2) Some of the counts are located in the * superblock rather than the cylinder group block. So, we focus our soft * updates implementation on protecting the bitmaps. When mounting a * filesystem, we recompute the auxiliary counts from the bitmaps. */ /* * Called just after updating the cylinder group block to allocate an inode. */ void softdep_setup_inomapdep(bp, ip, newinum) struct buf *bp; /* buffer for cylgroup block with inode map */ struct inode *ip; /* inode related to allocation */ ino_t newinum; /* new inode number being allocated */ { struct inodedep *inodedep; struct bmsafemap *bmsafemap; /* * Create a dependency for the newly allocated inode. * Panic if it already exists as something is seriously wrong. * Otherwise add it to the dependency list for the buffer holding * the cylinder group map from which it was allocated. */ ACQUIRE_LOCK(&lk); if (inodedep_lookup(ip->i_fs, newinum, DEPALLOC | NODELAY, &inodedep) != 0) { FREE_LOCK(&lk); panic("softdep_setup_inomapdep: found inode"); } inodedep->id_buf = bp; inodedep->id_state &= ~DEPCOMPLETE; bmsafemap = bmsafemap_lookup(bp); LIST_INSERT_HEAD(&bmsafemap->sm_inodedephd, inodedep, id_deps); FREE_LOCK(&lk); } /* * Called just after updating the cylinder group block to * allocate block or fragment. */ void softdep_setup_blkmapdep(bp, fs, newblkno) struct buf *bp; /* buffer for cylgroup block with block map */ struct fs *fs; /* filesystem doing allocation */ daddr_t newblkno; /* number of newly allocated block */ { struct newblk *newblk; struct bmsafemap *bmsafemap; /* * Create a dependency for the newly allocated block. * Add it to the dependency list for the buffer holding * the cylinder group map from which it was allocated. */ if (newblk_lookup(fs, newblkno, DEPALLOC, &newblk) != 0) panic("softdep_setup_blkmapdep: found block"); ACQUIRE_LOCK(&lk); newblk->nb_bmsafemap = bmsafemap = bmsafemap_lookup(bp); LIST_INSERT_HEAD(&bmsafemap->sm_newblkhd, newblk, nb_deps); FREE_LOCK(&lk); } /* * Find the bmsafemap associated with a cylinder group buffer. * If none exists, create one. The buffer must be locked when * this routine is called and this routine must be called with * splbio interrupts blocked. */ STATIC struct bmsafemap * bmsafemap_lookup(bp) struct buf *bp; { struct bmsafemap *bmsafemap; struct worklist *wk; #ifdef DEBUG if (lk.lkt_held == -1) panic("bmsafemap_lookup: lock not held"); #endif LIST_FOREACH(wk, &bp->b_dep, wk_list) if (wk->wk_type == D_BMSAFEMAP) return (WK_BMSAFEMAP(wk)); FREE_LOCK(&lk); bmsafemap = pool_get(&bmsafemap_pool, PR_WAITOK); bmsafemap->sm_list.wk_type = D_BMSAFEMAP; bmsafemap->sm_list.wk_state = 0; bmsafemap->sm_buf = bp; LIST_INIT(&bmsafemap->sm_allocdirecthd); LIST_INIT(&bmsafemap->sm_allocindirhd); LIST_INIT(&bmsafemap->sm_inodedephd); LIST_INIT(&bmsafemap->sm_newblkhd); ACQUIRE_LOCK(&lk); WORKLIST_INSERT(&bp->b_dep, &bmsafemap->sm_list); return (bmsafemap); } /* * Direct block allocation dependencies. * * When a new block is allocated, the corresponding disk locations must be * initialized (with zeros or new data) before the on-disk inode points to * them. Also, the freemap from which the block was allocated must be * updated (on disk) before the inode's pointer. These two dependencies are * independent of each other and are needed for all file blocks and indirect * blocks that are pointed to directly by the inode. Just before the * "in-core" version of the inode is updated with a newly allocated block * number, a procedure (below) is called to setup allocation dependency * structures. These structures are removed when the corresponding * dependencies are satisfied or when the block allocation becomes obsolete * (i.e., the file is deleted, the block is de-allocated, or the block is a * fragment that gets upgraded). All of these cases are handled in * procedures described later. * * When a file extension causes a fragment to be upgraded, either to a larger * fragment or to a full block, the on-disk location may change (if the * previous fragment could not simply be extended). In this case, the old * fragment must be de-allocated, but not until after the inode's pointer has * been updated. In most cases, this is handled by later procedures, which * will construct a "freefrag" structure to be added to the workitem queue * when the inode update is complete (or obsolete). The main exception to * this is when an allocation occurs while a pending allocation dependency * (for the same block pointer) remains. This case is handled in the main * allocation dependency setup procedure by immediately freeing the * unreferenced fragments. */ void softdep_setup_allocdirect(ip, lbn, newblkno, oldblkno, newsize, oldsize, bp) struct inode *ip; /* inode to which block is being added */ ufs_lbn_t lbn; /* block pointer within inode */ daddr_t newblkno; /* disk block number being added */ daddr_t oldblkno; /* previous block number, 0 unless frag */ long newsize; /* size of new block */ long oldsize; /* size of new block */ struct buf *bp; /* bp for allocated block */ { struct allocdirect *adp, *oldadp; struct allocdirectlst *adphead; struct bmsafemap *bmsafemap; struct inodedep *inodedep; struct pagedep *pagedep; struct newblk *newblk; adp = pool_get(&allocdirect_pool, PR_WAITOK); bzero(adp, sizeof(struct allocdirect)); adp->ad_list.wk_type = D_ALLOCDIRECT; adp->ad_lbn = lbn; adp->ad_newblkno = newblkno; adp->ad_oldblkno = oldblkno; adp->ad_newsize = newsize; adp->ad_oldsize = oldsize; adp->ad_state = ATTACHED; LIST_INIT(&adp->ad_newdirblk); if (newblkno == oldblkno) adp->ad_freefrag = NULL; else adp->ad_freefrag = newfreefrag(ip, oldblkno, oldsize); if (newblk_lookup(ip->i_fs, newblkno, 0, &newblk) == 0) panic("softdep_setup_allocdirect: lost block"); ACQUIRE_LOCK(&lk); inodedep_lookup(ip->i_fs, ip->i_number, DEPALLOC | NODELAY, &inodedep); adp->ad_inodedep = inodedep; if (newblk->nb_state == DEPCOMPLETE) { adp->ad_state |= DEPCOMPLETE; adp->ad_buf = NULL; } else { bmsafemap = newblk->nb_bmsafemap; adp->ad_buf = bmsafemap->sm_buf; LIST_REMOVE(newblk, nb_deps); LIST_INSERT_HEAD(&bmsafemap->sm_allocdirecthd, adp, ad_deps); } LIST_REMOVE(newblk, nb_hash); pool_put(&newblk_pool, newblk); if (bp == NULL) { /* * XXXUBC - Yes, I know how to fix this, but not right now. */ panic("softdep_setup_allocdirect: Bonk art in the head"); } WORKLIST_INSERT(&bp->b_dep, &adp->ad_list); if (lbn >= NDADDR) { /* allocating an indirect block */ if (oldblkno != 0) { FREE_LOCK(&lk); panic("softdep_setup_allocdirect: non-zero indir"); } } else { /* * Allocating a direct block. * * If we are allocating a directory block, then we must * allocate an associated pagedep to track additions and * deletions. */ if ((ip->i_ffs_mode & IFMT) == IFDIR && pagedep_lookup(ip, lbn, DEPALLOC, &pagedep) == 0) WORKLIST_INSERT(&bp->b_dep, &pagedep->pd_list); } /* * The list of allocdirects must be kept in sorted and ascending * order so that the rollback routines can quickly determine the * first uncommitted block (the size of the file stored on disk * ends at the end of the lowest committed fragment, or if there * are no fragments, at the end of the highest committed block). * Since files generally grow, the typical case is that the new * block is to be added at the end of the list. We speed this * special case by checking against the last allocdirect in the * list before laboriously traversing the list looking for the * insertion point. */ adphead = &inodedep->id_newinoupdt; oldadp = TAILQ_LAST(adphead, allocdirectlst); if (oldadp == NULL || oldadp->ad_lbn <= lbn) { /* insert at end of list */ TAILQ_INSERT_TAIL(adphead, adp, ad_next); if (oldadp != NULL && oldadp->ad_lbn == lbn) allocdirect_merge(adphead, adp, oldadp); FREE_LOCK(&lk); return; } TAILQ_FOREACH(oldadp, adphead, ad_next) { if (oldadp->ad_lbn >= lbn) break; } if (oldadp == NULL) { FREE_LOCK(&lk); panic("softdep_setup_allocdirect: lost entry"); } /* insert in middle of list */ TAILQ_INSERT_BEFORE(oldadp, adp, ad_next); if (oldadp->ad_lbn == lbn) allocdirect_merge(adphead, adp, oldadp); FREE_LOCK(&lk); } /* * Replace an old allocdirect dependency with a newer one. * This routine must be called with splbio interrupts blocked. */ STATIC void allocdirect_merge(adphead, newadp, oldadp) struct allocdirectlst *adphead; /* head of list holding allocdirects */ struct allocdirect *newadp; /* allocdirect being added */ struct allocdirect *oldadp; /* existing allocdirect being checked */ { struct worklist *wk; struct freefrag *freefrag; struct newdirblk *newdirblk; #ifdef DEBUG if (lk.lkt_held == -1) panic("allocdirect_merge: lock not held"); #endif if (newadp->ad_oldblkno != oldadp->ad_newblkno || newadp->ad_oldsize != oldadp->ad_newsize || newadp->ad_lbn >= NDADDR) { FREE_LOCK(&lk); panic("allocdirect_merge: old %d != new %d || lbn %ld >= %d", newadp->ad_oldblkno, oldadp->ad_newblkno, (long)newadp->ad_lbn, NDADDR); } newadp->ad_oldblkno = oldadp->ad_oldblkno; newadp->ad_oldsize = oldadp->ad_oldsize; /* * If the old dependency had a fragment to free or had never * previously had a block allocated, then the new dependency * can immediately post its freefrag and adopt the old freefrag. * This action is done by swapping the freefrag dependencies. * The new dependency gains the old one's freefrag, and the * old one gets the new one and then immediately puts it on * the worklist when it is freed by free_allocdirect. It is * not possible to do this swap when the old dependency had a * non-zero size but no previous fragment to free. This condition * arises when the new block is an extension of the old block. * Here, the first part of the fragment allocated to the new * dependency is part of the block currently claimed on disk by * the old dependency, so cannot legitimately be freed until the * conditions for the new dependency are fulfilled. */ if (oldadp->ad_freefrag != NULL || oldadp->ad_oldblkno == 0) { freefrag = newadp->ad_freefrag; newadp->ad_freefrag = oldadp->ad_freefrag; oldadp->ad_freefrag = freefrag; } /* * If we are tracking a new directory-block allocation, * move it from the old allocdirect to the new allocdirect. */ if ((wk = LIST_FIRST(&oldadp->ad_newdirblk)) != NULL) { newdirblk = WK_NEWDIRBLK(wk); WORKLIST_REMOVE(&newdirblk->db_list); if (LIST_FIRST(&oldadp->ad_newdirblk) != NULL) panic("allocdirect_merge: extra newdirblk"); WORKLIST_INSERT(&newadp->ad_newdirblk, &newdirblk->db_list); } free_allocdirect(adphead, oldadp, 0); } /* * Allocate a new freefrag structure if needed. */ STATIC struct freefrag * newfreefrag(ip, blkno, size) struct inode *ip; daddr_t blkno; long size; { struct freefrag *freefrag; struct fs *fs; if (blkno == 0) return (NULL); fs = ip->i_fs; if (fragnum(fs, blkno) + numfrags(fs, size) > fs->fs_frag) panic("newfreefrag: frag size"); freefrag = pool_get(&freefrag_pool, PR_WAITOK); freefrag->ff_list.wk_type = D_FREEFRAG; freefrag->ff_state = ip->i_ffs_uid & ~ONWORKLIST; /* XXX - used below */ freefrag->ff_inum = ip->i_number; freefrag->ff_mnt = ITOV(ip)->v_mount; freefrag->ff_devvp = ip->i_devvp; freefrag->ff_blkno = blkno; freefrag->ff_fragsize = size; return (freefrag); } /* * This workitem de-allocates fragments that were replaced during * file block allocation. */ STATIC void handle_workitem_freefrag(freefrag) struct freefrag *freefrag; { struct inode tip; tip.i_vnode = NULL; tip.i_fs = VFSTOUFS(freefrag->ff_mnt)->um_fs; tip.i_ump = VFSTOUFS(freefrag->ff_mnt); tip.i_dev = freefrag->ff_devvp->v_rdev; tip.i_number = freefrag->ff_inum; tip.i_ffs_uid = freefrag->ff_state & ~ONWORKLIST; /* XXX - set above */ ffs_blkfree(&tip, freefrag->ff_blkno, freefrag->ff_fragsize); pool_put(&freefrag_pool, freefrag); } /* * Indirect block allocation dependencies. * * The same dependencies that exist for a direct block also exist when * a new block is allocated and pointed to by an entry in a block of * indirect pointers. The undo/redo states described above are also * used here. Because an indirect block contains many pointers that * may have dependencies, a second copy of the entire in-memory indirect * block is kept. The buffer cache copy is always completely up-to-date. * The second copy, which is used only as a source for disk writes, * contains only the safe pointers (i.e., those that have no remaining * update dependencies). The second copy is freed when all pointers * are safe. The cache is not allowed to replace indirect blocks with * pending update dependencies. If a buffer containing an indirect * block with dependencies is written, these routines will mark it * dirty again. It can only be successfully written once all the * dependencies are removed. The ffs_fsync routine in conjunction with * softdep_sync_metadata work together to get all the dependencies * removed so that a file can be successfully written to disk. Three * procedures are used when setting up indirect block pointer * dependencies. The division is necessary because of the organization * of the "balloc" routine and because of the distinction between file * pages and file metadata blocks. */ /* * Allocate a new allocindir structure. */ STATIC struct allocindir * newallocindir(ip, ptrno, newblkno, oldblkno) struct inode *ip; /* inode for file being extended */ int ptrno; /* offset of pointer in indirect block */ daddr_t newblkno; /* disk block number being added */ daddr_t oldblkno; /* previous block number, 0 if none */ { struct allocindir *aip; aip = pool_get(&allocindir_pool, PR_WAITOK); bzero(aip,sizeof(struct allocindir)); aip->ai_list.wk_type = D_ALLOCINDIR; aip->ai_state = ATTACHED; aip->ai_offset = ptrno; aip->ai_newblkno = newblkno; aip->ai_oldblkno = oldblkno; aip->ai_freefrag = newfreefrag(ip, oldblkno, ip->i_fs->fs_bsize); return (aip); } /* * Called just before setting an indirect block pointer * to a newly allocated file page. */ void softdep_setup_allocindir_page(ip, lbn, bp, ptrno, newblkno, oldblkno, nbp) struct inode *ip; /* inode for file being extended */ ufs_lbn_t lbn; /* allocated block number within file */ struct buf *bp; /* buffer with indirect blk referencing page */ int ptrno; /* offset of pointer in indirect block */ daddr_t newblkno; /* disk block number being added */ daddr_t oldblkno; /* previous block number, 0 if none */ struct buf *nbp; /* buffer holding allocated page */ { struct allocindir *aip; struct pagedep *pagedep; aip = newallocindir(ip, ptrno, newblkno, oldblkno); ACQUIRE_LOCK(&lk); /* * If we are allocating a directory page, then we must * allocate an associated pagedep to track additions and * deletions. */ if ((ip->i_ffs_mode & IFMT) == IFDIR && pagedep_lookup(ip, lbn, DEPALLOC, &pagedep) == 0) WORKLIST_INSERT(&nbp->b_dep, &pagedep->pd_list); if (nbp == NULL) { /* * XXXUBC - Yes, I know how to fix this, but not right now. */ panic("softdep_setup_allocindir_page: Bonk art in the head"); } WORKLIST_INSERT(&nbp->b_dep, &aip->ai_list); FREE_LOCK(&lk); setup_allocindir_phase2(bp, ip, aip); } /* * Called just before setting an indirect block pointer to a * newly allocated indirect block. */ void softdep_setup_allocindir_meta(nbp, ip, bp, ptrno, newblkno) struct buf *nbp; /* newly allocated indirect block */ struct inode *ip; /* inode for file being extended */ struct buf *bp; /* indirect block referencing allocated block */ int ptrno; /* offset of pointer in indirect block */ daddr_t newblkno; /* disk block number being added */ { struct allocindir *aip; aip = newallocindir(ip, ptrno, newblkno, 0); ACQUIRE_LOCK(&lk); WORKLIST_INSERT(&nbp->b_dep, &aip->ai_list); FREE_LOCK(&lk); setup_allocindir_phase2(bp, ip, aip); } /* * Called to finish the allocation of the "aip" allocated * by one of the two routines above. */ STATIC void setup_allocindir_phase2(bp, ip, aip) struct buf *bp; /* in-memory copy of the indirect block */ struct inode *ip; /* inode for file being extended */ struct allocindir *aip; /* allocindir allocated by the above routines */ { struct worklist *wk; struct indirdep *indirdep, *newindirdep; struct bmsafemap *bmsafemap; struct allocindir *oldaip; struct freefrag *freefrag; struct newblk *newblk; if (bp->b_lblkno >= 0) panic("setup_allocindir_phase2: not indir blk"); for (indirdep = NULL, newindirdep = NULL; ; ) { ACQUIRE_LOCK(&lk); LIST_FOREACH(wk, &bp->b_dep, wk_list) { if (wk->wk_type != D_INDIRDEP) continue; indirdep = WK_INDIRDEP(wk); break; } if (indirdep == NULL && newindirdep) { indirdep = newindirdep; WORKLIST_INSERT(&bp->b_dep, &indirdep->ir_list); newindirdep = NULL; } FREE_LOCK(&lk); if (indirdep) { if (newblk_lookup(ip->i_fs, aip->ai_newblkno, 0, &newblk) == 0) panic("setup_allocindir: lost block"); ACQUIRE_LOCK(&lk); if (newblk->nb_state == DEPCOMPLETE) { aip->ai_state |= DEPCOMPLETE; aip->ai_buf = NULL; } else { bmsafemap = newblk->nb_bmsafemap; aip->ai_buf = bmsafemap->sm_buf; LIST_REMOVE(newblk, nb_deps); LIST_INSERT_HEAD(&bmsafemap->sm_allocindirhd, aip, ai_deps); } LIST_REMOVE(newblk, nb_hash); pool_put(&newblk_pool, newblk); aip->ai_indirdep = indirdep; /* * Check to see if there is an existing dependency * for this block. If there is, merge the old * dependency into the new one. */ if (aip->ai_oldblkno == 0) oldaip = NULL; else LIST_FOREACH(oldaip, &indirdep->ir_deplisthd, ai_next) if (oldaip->ai_offset == aip->ai_offset) break; freefrag = NULL; if (oldaip != NULL) { if (oldaip->ai_newblkno != aip->ai_oldblkno) { FREE_LOCK(&lk); panic("setup_allocindir_phase2: blkno"); } aip->ai_oldblkno = oldaip->ai_oldblkno; freefrag = aip->ai_freefrag; aip->ai_freefrag = oldaip->ai_freefrag; oldaip->ai_freefrag = NULL; free_allocindir(oldaip, NULL); } LIST_INSERT_HEAD(&indirdep->ir_deplisthd, aip, ai_next); ((daddr_t *)indirdep->ir_savebp->b_data) [aip->ai_offset] = aip->ai_oldblkno; FREE_LOCK(&lk); if (freefrag != NULL) handle_workitem_freefrag(freefrag); } if (newindirdep) { if (indirdep->ir_savebp != NULL) brelse(newindirdep->ir_savebp); WORKITEM_FREE(newindirdep, D_INDIRDEP); } if (indirdep) break; newindirdep = pool_get(&indirdep_pool, PR_WAITOK); newindirdep->ir_list.wk_type = D_INDIRDEP; newindirdep->ir_state = ATTACHED; LIST_INIT(&newindirdep->ir_deplisthd); LIST_INIT(&newindirdep->ir_donehd); if (bp->b_blkno == bp->b_lblkno) { VOP_BMAP(bp->b_vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL); } newindirdep->ir_savebp = getblk(ip->i_devvp, bp->b_blkno, bp->b_bcount, 0, 0); #if 0 BUF_KERNPROC(newindirdep->ir_savebp); #endif bcopy(bp->b_data, newindirdep->ir_savebp->b_data, bp->b_bcount); } } /* * Block de-allocation dependencies. * * When blocks are de-allocated, the on-disk pointers must be nullified before * the blocks are made available for use by other files. (The true * requirement is that old pointers must be nullified before new on-disk * pointers are set. We chose this slightly more stringent requirement to * reduce complexity.) Our implementation handles this dependency by updating * the inode (or indirect block) appropriately but delaying the actual block * de-allocation (i.e., freemap and free space count manipulation) until * after the updated versions reach stable storage. After the disk is * updated, the blocks can be safely de-allocated whenever it is convenient. * This implementation handles only the common case of reducing a file's * length to zero. Other cases are handled by the conventional synchronous * write approach. * * The ffs implementation with which we worked double-checks * the state of the block pointers and file size as it reduces * a file's length. Some of this code is replicated here in our * soft updates implementation. The freeblks->fb_chkcnt field is * used to transfer a part of this information to the procedure * that eventually de-allocates the blocks. * * This routine should be called from the routine that shortens * a file's length, before the inode's size or block pointers * are modified. It will save the block pointer information for * later release and zero the inode so that the calling routine * can release it. */ void softdep_setup_freeblocks(ip, length) struct inode *ip; /* The inode whose length is to be reduced */ off_t length; /* The new length for the file */ { struct freeblks *freeblks; struct inodedep *inodedep; struct allocdirect *adp; struct vnode *vp; struct buf *bp; struct fs *fs; int i, delay, error; fs = ip->i_fs; if (length != 0) panic("softdep_setup_freeblocks: non-zero length"); freeblks = pool_get(&freeblks_pool, PR_WAITOK); bzero(freeblks, sizeof(struct freeblks)); freeblks->fb_list.wk_type = D_FREEBLKS; freeblks->fb_state = ATTACHED; freeblks->fb_uid = ip->i_ffs_uid; freeblks->fb_previousinum = ip->i_number; freeblks->fb_devvp = ip->i_devvp; freeblks->fb_mnt = ITOV(ip)->v_mount; freeblks->fb_oldsize = ip->i_ffs_size; freeblks->fb_newsize = length; freeblks->fb_chkcnt = ip->i_ffs_blocks; for (i = 0; i < NDADDR; i++) { freeblks->fb_dblks[i] = ip->i_ffs_db[i]; ip->i_ffs_db[i] = 0; } for (i = 0; i < NIADDR; i++) { freeblks->fb_iblks[i] = ip->i_ffs_ib[i]; ip->i_ffs_ib[i] = 0; } ip->i_ffs_blocks = 0; ip->i_ffs_size = 0; /* * Push the zero'ed inode to to its disk buffer so that we are free * to delete its dependencies below. Once the dependencies are gone * the buffer can be safely released. */ if ((error = bread(ip->i_devvp, fsbtodb(fs, ino_to_fsba(fs, ip->i_number)), (int)fs->fs_bsize, NOCRED, &bp)) != 0) softdep_error("softdep_setup_freeblocks", error); *((struct ufs1_dinode *)bp->b_data + ino_to_fsbo(fs, ip->i_number)) = ip->i_din1; /* * Find and eliminate any inode dependencies. */ ACQUIRE_LOCK(&lk); (void) inodedep_lookup(fs, ip->i_number, DEPALLOC, &inodedep); if ((inodedep->id_state & IOSTARTED) != 0) { FREE_LOCK(&lk); panic("softdep_setup_freeblocks: inode busy"); } /* * Add the freeblks structure to the list of operations that * must await the zero'ed inode being written to disk. If we * still have a bitmap dependency (delay == 0), then the inode * has never been written to disk, so we can process the * freeblks below once we have deleted the dependencies. */ delay = (inodedep->id_state & DEPCOMPLETE); if (delay) WORKLIST_INSERT(&inodedep->id_bufwait, &freeblks->fb_list); /* * Because the file length has been truncated to zero, any * pending block allocation dependency structures associated * with this inode are obsolete and can simply be de-allocated. * We must first merge the two dependency lists to get rid of * any duplicate freefrag structures, then purge the merged list. * If we still have a bitmap dependency, then the inode has never * been written to disk, so we can free any fragments without delay. */ merge_inode_lists(inodedep); while ((adp = TAILQ_FIRST(&inodedep->id_inoupdt)) != 0) free_allocdirect(&inodedep->id_inoupdt, adp, delay); FREE_LOCK(&lk); bdwrite(bp); /* * We must wait for any I/O in progress to finish so that * all potential buffers on the dirty list will be visible. * Once they are all there, walk the list and get rid of * any dependencies. */ vp = ITOV(ip); ACQUIRE_LOCK(&lk); drain_output(vp, 1); while ((bp = LIST_FIRST(&vp->v_dirtyblkhd))) { if (!getdirtybuf(&bp, MNT_WAIT)) break; (void) inodedep_lookup(fs, ip->i_number, 0, &inodedep); deallocate_dependencies(bp, inodedep); bp->b_flags |= B_INVAL | B_NOCACHE; FREE_LOCK(&lk); brelse(bp); ACQUIRE_LOCK(&lk); } if (inodedep_lookup(fs, ip->i_number, 0, &inodedep) != 0) (void) free_inodedep(inodedep); if (delay) { freeblks->fb_state |= DEPCOMPLETE; /* * If the inode with zeroed block pointers is now on disk we * can start freeing blocks. Add freeblks to the worklist * instead of calling handle_workitem_freeblocks() directly as * it is more likely that additional IO is needed to complete * the request than in the !delay case. */ if ((freeblks->fb_state & ALLCOMPLETE) == ALLCOMPLETE) add_to_worklist(&freeblks->fb_list); } FREE_LOCK(&lk); /* * If the inode has never been written to disk (delay == 0), * then we can process the freeblks now that we have deleted * the dependencies. */ if (!delay) handle_workitem_freeblocks(freeblks); } /* * Reclaim any dependency structures from a buffer that is about to * be reallocated to a new vnode. The buffer must be locked, thus, * no I/O completion operations can occur while we are manipulating * its associated dependencies. The mutex is held so that other I/O's * associated with related dependencies do not occur. */ STATIC void deallocate_dependencies(bp, inodedep) struct buf *bp; struct inodedep *inodedep; { struct worklist *wk; struct indirdep *indirdep; struct allocindir *aip; struct pagedep *pagedep; struct dirrem *dirrem; struct diradd *dap; int i; while ((wk = LIST_FIRST(&bp->b_dep)) != NULL) { switch (wk->wk_type) { case D_INDIRDEP: indirdep = WK_INDIRDEP(wk); /* * None of the indirect pointers will ever be visible, * so they can simply be tossed. GOINGAWAY ensures * that allocated pointers will be saved in the buffer * cache until they are freed. Note that they will * only be able to be found by their physical address * since the inode mapping the logical address will * be gone. The save buffer used for the safe copy * was allocated in setup_allocindir_phase2 using * the physical address so it could be used for this * purpose. Hence we swap the safe copy with the real * copy, allowing the safe copy to be freed and holding * on to the real copy for later use in indir_trunc. */ if (indirdep->ir_state & GOINGAWAY) { FREE_LOCK(&lk); panic("deallocate_dependencies: already gone"); } indirdep->ir_state |= GOINGAWAY; while ((aip = LIST_FIRST(&indirdep->ir_deplisthd)) != 0) free_allocindir(aip, inodedep); if (bp->b_lblkno >= 0 || bp->b_blkno != indirdep->ir_savebp->b_lblkno) { FREE_LOCK(&lk); panic("deallocate_dependencies: not indir"); } bcopy(bp->b_data, indirdep->ir_savebp->b_data, bp->b_bcount); WORKLIST_REMOVE(wk); WORKLIST_INSERT(&indirdep->ir_savebp->b_dep, wk); continue; case D_PAGEDEP: pagedep = WK_PAGEDEP(wk); /* * None of the directory additions will ever be * visible, so they can simply be tossed. */ for (i = 0; i < DAHASHSZ; i++) while ((dap = LIST_FIRST(&pagedep->pd_diraddhd[i]))) free_diradd(dap); while ((dap = LIST_FIRST(&pagedep->pd_pendinghd)) != 0) free_diradd(dap); /* * Copy any directory remove dependencies to the list * to be processed after the zero'ed inode is written. * If the inode has already been written, then they * can be dumped directly onto the work list. */ while ((dirrem = LIST_FIRST(&pagedep->pd_dirremhd))) { LIST_REMOVE(dirrem, dm_next); dirrem->dm_dirinum = pagedep->pd_ino; if (inodedep == NULL || (inodedep->id_state & ALLCOMPLETE) == ALLCOMPLETE) add_to_worklist(&dirrem->dm_list); else WORKLIST_INSERT(&inodedep->id_bufwait, &dirrem->dm_list); } if ((pagedep->pd_state & NEWBLOCK) != 0) { LIST_FOREACH(wk, &inodedep->id_bufwait, wk_list) if (wk->wk_type == D_NEWDIRBLK && WK_NEWDIRBLK(wk)->db_pagedep == pagedep) break; if (wk != NULL) { WORKLIST_REMOVE(wk); free_newdirblk(WK_NEWDIRBLK(wk)); } else { FREE_LOCK(&lk); panic("deallocate_dependencies: " "lost pagedep"); } } WORKLIST_REMOVE(&pagedep->pd_list); LIST_REMOVE(pagedep, pd_hash); WORKITEM_FREE(pagedep, D_PAGEDEP); continue; case D_ALLOCINDIR: free_allocindir(WK_ALLOCINDIR(wk), inodedep); continue; case D_ALLOCDIRECT: case D_INODEDEP: FREE_LOCK(&lk); panic("deallocate_dependencies: Unexpected type %s", TYPENAME(wk->wk_type)); /* NOTREACHED */ default: FREE_LOCK(&lk); panic("deallocate_dependencies: Unknown type %s", TYPENAME(wk->wk_type)); /* NOTREACHED */ } } } /* * Free an allocdirect. Generate a new freefrag work request if appropriate. * This routine must be called with splbio interrupts blocked. */ STATIC void free_allocdirect(adphead, adp, delay) struct allocdirectlst *adphead; struct allocdirect *adp; int delay; { struct newdirblk *newdirblk; struct worklist *wk; #ifdef DEBUG if (lk.lkt_held == -1) panic("free_allocdirect: lock not held"); #endif if ((adp->ad_state & DEPCOMPLETE) == 0) LIST_REMOVE(adp, ad_deps); TAILQ_REMOVE(adphead, adp, ad_next); if ((adp->ad_state & COMPLETE) == 0) WORKLIST_REMOVE(&adp->ad_list); if (adp->ad_freefrag != NULL) { if (delay) WORKLIST_INSERT(&adp->ad_inodedep->id_bufwait, &adp->ad_freefrag->ff_list); else add_to_worklist(&adp->ad_freefrag->ff_list); } if ((wk = LIST_FIRST(&adp->ad_newdirblk)) != NULL) { newdirblk = WK_NEWDIRBLK(wk); WORKLIST_REMOVE(&newdirblk->db_list); if (LIST_FIRST(&adp->ad_newdirblk) != NULL) panic("free_allocdirect: extra newdirblk"); if (delay) WORKLIST_INSERT(&adp->ad_inodedep->id_bufwait, &newdirblk->db_list); else free_newdirblk(newdirblk); } WORKITEM_FREE(adp, D_ALLOCDIRECT); } /* * Free a newdirblk. Clear the NEWBLOCK flag on its associated pagedep. * This routine must be called with splbio interrupts blocked. */ void free_newdirblk(newdirblk) struct newdirblk *newdirblk; { struct pagedep *pagedep; struct diradd *dap; int i; #ifdef DEBUG if (lk.lkt_held == -1) panic("free_newdirblk: lock not held"); #endif /* * If the pagedep is still linked onto the directory buffer * dependency chain, then some of the entries on the * pd_pendinghd list may not be committed to disk yet. In * this case, we will simply clear the NEWBLOCK flag and * let the pd_pendinghd list be processed when the pagedep * is next written. If the pagedep is no longer on the buffer * dependency chain, then all the entries on the pd_pending * list are committed to disk and we can free them here. */ pagedep = newdirblk->db_pagedep; pagedep->pd_state &= ~NEWBLOCK; if ((pagedep->pd_state & ONWORKLIST) == 0) while ((dap = LIST_FIRST(&pagedep->pd_pendinghd)) != NULL) free_diradd(dap); /* * If no dependencies remain, the pagedep will be freed. */ for (i = 0; i < DAHASHSZ; i++) if (LIST_FIRST(&pagedep->pd_diraddhd[i]) != NULL) break; if (i == DAHASHSZ && (pagedep->pd_state & ONWORKLIST) == 0) { LIST_REMOVE(pagedep, pd_hash); WORKITEM_FREE(pagedep, D_PAGEDEP); } WORKITEM_FREE(newdirblk, D_NEWDIRBLK); } /* * Prepare an inode to be freed. The actual free operation is not * done until the zero'ed inode has been written to disk. */ void softdep_freefile(pvp, ino, mode) struct vnode *pvp; ino_t ino; mode_t mode; { struct inode *ip = VTOI(pvp); struct inodedep *inodedep; struct freefile *freefile; /* * This sets up the inode de-allocation dependency. */ freefile = pool_get(&freefile_pool, PR_WAITOK); freefile->fx_list.wk_type = D_FREEFILE; freefile->fx_list.wk_state = 0; freefile->fx_mode = mode; freefile->fx_oldinum = ino; freefile->fx_devvp = ip->i_devvp; freefile->fx_mnt = ITOV(ip)->v_mount; /* * If the inodedep does not exist, then the zero'ed inode has * been written to disk. If the allocated inode has never been * written to disk, then the on-disk inode is zero'ed. In either * case we can free the file immediately. */ ACQUIRE_LOCK(&lk); if (inodedep_lookup(ip->i_fs, ino, 0, &inodedep) == 0 || check_inode_unwritten(inodedep)) { FREE_LOCK(&lk); handle_workitem_freefile(freefile); return; } WORKLIST_INSERT(&inodedep->id_inowait, &freefile->fx_list); FREE_LOCK(&lk); } /* * Check to see if an inode has never been written to disk. If * so free the inodedep and return success, otherwise return failure. * This routine must be called with splbio interrupts blocked. * * If we still have a bitmap dependency, then the inode has never * been written to disk. Drop the dependency as it is no longer * necessary since the inode is being deallocated. We set the * ALLCOMPLETE flags since the bitmap now properly shows that the * inode is not allocated. Even if the inode is actively being * written, it has been rolled back to its zero'ed state, so we * are ensured that a zero inode is what is on the disk. For short * lived files, this change will usually result in removing all the * dependencies from the inode so that it can be freed immediately. */ STATIC int check_inode_unwritten(inodedep) struct inodedep *inodedep; { if ((inodedep->id_state & DEPCOMPLETE) != 0 || LIST_FIRST(&inodedep->id_pendinghd) != NULL || LIST_FIRST(&inodedep->id_bufwait) != NULL || LIST_FIRST(&inodedep->id_inowait) != NULL || TAILQ_FIRST(&inodedep->id_inoupdt) != NULL || TAILQ_FIRST(&inodedep->id_newinoupdt) != NULL || inodedep->id_nlinkdelta != 0) return (0); inodedep->id_state |= ALLCOMPLETE; LIST_REMOVE(inodedep, id_deps); inodedep->id_buf = NULL; if (inodedep->id_state & ONWORKLIST) WORKLIST_REMOVE(&inodedep->id_list); if (inodedep->id_savedino != NULL) { FREE(inodedep->id_savedino, M_INODEDEP); inodedep->id_savedino = NULL; } if (free_inodedep(inodedep) == 0) { FREE_LOCK(&lk); panic("check_inode_unwritten: busy inode"); } return (1); } /* * Try to free an inodedep structure. Return 1 if it could be freed. */ STATIC int free_inodedep(inodedep) struct inodedep *inodedep; { if ((inodedep->id_state & ONWORKLIST) != 0 || (inodedep->id_state & ALLCOMPLETE) != ALLCOMPLETE || LIST_FIRST(&inodedep->id_pendinghd) != NULL || LIST_FIRST(&inodedep->id_bufwait) != NULL || LIST_FIRST(&inodedep->id_inowait) != NULL || TAILQ_FIRST(&inodedep->id_inoupdt) != NULL || TAILQ_FIRST(&inodedep->id_newinoupdt) != NULL || inodedep->id_nlinkdelta != 0 || inodedep->id_savedino != NULL) return (0); LIST_REMOVE(inodedep, id_hash); WORKITEM_FREE(inodedep, D_INODEDEP); num_inodedep -= 1; return (1); } /* * This workitem routine performs the block de-allocation. * The workitem is added to the pending list after the updated * inode block has been written to disk. As mentioned above, * checks regarding the number of blocks de-allocated (compared * to the number of blocks allocated for the file) are also * performed in this function. */ STATIC void handle_workitem_freeblocks(freeblks) struct freeblks *freeblks; { struct inode tip; daddr_t bn; struct fs *fs; int i, level, bsize; long nblocks, blocksreleased = 0; int error, allerror = 0; ufs_lbn_t baselbns[NIADDR], tmpval; tip.i_fs = fs = VFSTOUFS(freeblks->fb_mnt)->um_fs; tip.i_number = freeblks->fb_previousinum; tip.i_ump = VFSTOUFS(freeblks->fb_mnt); tip.i_dev = freeblks->fb_devvp->v_rdev; tip.i_ffs_size = freeblks->fb_oldsize; tip.i_ffs_uid = freeblks->fb_uid; tip.i_vnode = NULL; tmpval = 1; baselbns[0] = NDADDR; for (i = 1; i < NIADDR; i++) { tmpval *= NINDIR(fs); baselbns[i] = baselbns[i - 1] + tmpval; } nblocks = btodb(fs->fs_bsize); blocksreleased = 0; /* * Indirect blocks first. */ for (level = (NIADDR - 1); level >= 0; level--) { if ((bn = freeblks->fb_iblks[level]) == 0) continue; if ((error = indir_trunc(&tip, fsbtodb(fs, bn), level, baselbns[level], &blocksreleased)) != 0) allerror = error; ffs_blkfree(&tip, bn, fs->fs_bsize); blocksreleased += nblocks; } /* * All direct blocks or frags. */ for (i = (NDADDR - 1); i >= 0; i--) { if ((bn = freeblks->fb_dblks[i]) == 0) continue; bsize = blksize(fs, &tip, i); ffs_blkfree(&tip, bn, bsize); blocksreleased += btodb(bsize); } #ifdef DIAGNOSTIC if (freeblks->fb_chkcnt != blocksreleased) printf("handle_workitem_freeblocks: block count\n"); if (allerror) softdep_error("handle_workitem_freeblks", allerror); #endif /* DIAGNOSTIC */ WORKITEM_FREE(freeblks, D_FREEBLKS); } /* * Release blocks associated with the inode ip and stored in the indirect * block dbn. If level is greater than SINGLE, the block is an indirect block * and recursive calls to indirtrunc must be used to cleanse other indirect * blocks. */ STATIC int indir_trunc(ip, dbn, level, lbn, countp) struct inode *ip; daddr_t dbn; int level; ufs_lbn_t lbn; long *countp; { struct buf *bp; daddr_t *bap; daddr_t nb; struct fs *fs; struct worklist *wk; struct indirdep *indirdep; int i, lbnadd, nblocks; int error, allerror = 0; fs = ip->i_fs; lbnadd = 1; for (i = level; i > 0; i--) lbnadd *= NINDIR(fs); /* * Get buffer of block pointers to be freed. This routine is not * called until the zero'ed inode has been written, so it is safe * to free blocks as they are encountered. Because the inode has * been zero'ed, calls to bmap on these blocks will fail. So, we * have to use the on-disk address and the block device for the * filesystem to look them up. If the file was deleted before its * indirect blocks were all written to disk, the routine that set * us up (deallocate_dependencies) will have arranged to leave * a complete copy of the indirect block in memory for our use. * Otherwise we have to read the blocks in from the disk. */ ACQUIRE_LOCK(&lk); if ((bp = incore(ip->i_devvp, dbn)) != NULL && (wk = LIST_FIRST(&bp->b_dep)) != NULL) { if (wk->wk_type != D_INDIRDEP || (indirdep = WK_INDIRDEP(wk))->ir_savebp != bp || (indirdep->ir_state & GOINGAWAY) == 0) { FREE_LOCK(&lk); panic("indir_trunc: lost indirdep"); } WORKLIST_REMOVE(wk); WORKITEM_FREE(indirdep, D_INDIRDEP); if (LIST_FIRST(&bp->b_dep) != NULL) { FREE_LOCK(&lk); panic("indir_trunc: dangling dep"); } FREE_LOCK(&lk); } else { FREE_LOCK(&lk); error = bread(ip->i_devvp, dbn, (int)fs->fs_bsize, NOCRED, &bp); if (error) return (error); } /* * Recursively free indirect blocks. */ bap = (daddr_t *)bp->b_data; nblocks = btodb(fs->fs_bsize); for (i = NINDIR(fs) - 1; i >= 0; i--) { if ((nb = bap[i]) == 0) continue; if (level != 0) { if ((error = indir_trunc(ip, fsbtodb(fs, nb), level - 1, lbn + (i * lbnadd), countp)) != 0) allerror = error; } ffs_blkfree(ip, nb, fs->fs_bsize); *countp += nblocks; } bp->b_flags |= B_INVAL | B_NOCACHE; brelse(bp); return (allerror); } /* * Free an allocindir. * This routine must be called with splbio interrupts blocked. */ STATIC void free_allocindir(aip, inodedep) struct allocindir *aip; struct inodedep *inodedep; { struct freefrag *freefrag; #ifdef DEBUG if (lk.lkt_held == -1) panic("free_allocindir: lock not held"); #endif if ((aip->ai_state & DEPCOMPLETE) == 0) LIST_REMOVE(aip, ai_deps); if (aip->ai_state & ONWORKLIST) WORKLIST_REMOVE(&aip->ai_list); LIST_REMOVE(aip, ai_next); if ((freefrag = aip->ai_freefrag) != NULL) { if (inodedep == NULL) add_to_worklist(&freefrag->ff_list); else WORKLIST_INSERT(&inodedep->id_bufwait, &freefrag->ff_list); } WORKITEM_FREE(aip, D_ALLOCINDIR); } /* * Directory entry addition dependencies. * * When adding a new directory entry, the inode (with its incremented link * count) must be written to disk before the directory entry's pointer to it. * Also, if the inode is newly allocated, the corresponding freemap must be * updated (on disk) before the directory entry's pointer. These requirements * are met via undo/redo on the directory entry's pointer, which consists * simply of the inode number. * * As directory entries are added and deleted, the free space within a * directory block can become fragmented. The ufs filesystem will compact * a fragmented directory block to make space for a new entry. When this * occurs, the offsets of previously added entries change. Any "diradd" * dependency structures corresponding to these entries must be updated with * the new offsets. */ /* * This routine is called after the in-memory inode's link * count has been incremented, but before the directory entry's * pointer to the inode has been set. */ int softdep_setup_directory_add(bp, dp, diroffset, newinum, newdirbp, isnewblk) struct buf *bp; /* buffer containing directory block */ struct inode *dp; /* inode for directory */ off_t diroffset; /* offset of new entry in directory */ long newinum; /* inode referenced by new directory entry */ struct buf *newdirbp; /* non-NULL => contents of new mkdir */ int isnewblk; /* entry is in a newly allocated block */ { int offset; /* offset of new entry within directory block */ ufs_lbn_t lbn; /* block in directory containing new entry */ struct fs *fs; struct diradd *dap; struct allocdirect *adp; struct pagedep *pagedep; struct inodedep *inodedep; struct newdirblk *newdirblk = NULL; struct mkdir *mkdir1, *mkdir2; fs = dp->i_fs; lbn = lblkno(fs, diroffset); offset = blkoff(fs, diroffset); dap = pool_get(&diradd_pool, PR_WAITOK); bzero(dap,sizeof(struct diradd)); dap->da_list.wk_type = D_DIRADD; dap->da_offset = offset; dap->da_newinum = newinum; dap->da_state = ATTACHED; if (isnewblk && lbn < NDADDR && fragoff(fs, diroffset) == 0) { newdirblk = pool_get(&newdirblk_pool, PR_WAITOK); newdirblk->db_list.wk_type = D_NEWDIRBLK; newdirblk->db_state = 0; } if (newdirbp == NULL) { dap->da_state |= DEPCOMPLETE; ACQUIRE_LOCK(&lk); } else { dap->da_state |= MKDIR_BODY | MKDIR_PARENT; mkdir1 = pool_get(&mkdir_pool, PR_WAITOK); mkdir1->md_list.wk_type = D_MKDIR; mkdir1->md_state = MKDIR_BODY; mkdir1->md_diradd = dap; mkdir2 = pool_get(&mkdir_pool, PR_WAITOK); mkdir2->md_list.wk_type = D_MKDIR; mkdir2->md_state = MKDIR_PARENT; mkdir2->md_diradd = dap; /* * Dependency on "." and ".." being written to disk. */ mkdir1->md_buf = newdirbp; ACQUIRE_LOCK(&lk); LIST_INSERT_HEAD(&mkdirlisthd, mkdir1, md_mkdirs); WORKLIST_INSERT(&newdirbp->b_dep, &mkdir1->md_list); FREE_LOCK(&lk); bdwrite(newdirbp); /* * Dependency on link count increase for parent directory */ ACQUIRE_LOCK(&lk); if (inodedep_lookup(fs, dp->i_number, 0, &inodedep) == 0 || (inodedep->id_state & ALLCOMPLETE) == ALLCOMPLETE) { dap->da_state &= ~MKDIR_PARENT; WORKITEM_FREE(mkdir2, D_MKDIR); } else { LIST_INSERT_HEAD(&mkdirlisthd, mkdir2, md_mkdirs); WORKLIST_INSERT(&inodedep->id_bufwait,&mkdir2->md_list); } } /* * Link into parent directory pagedep to await its being written. */ if (pagedep_lookup(dp, lbn, DEPALLOC, &pagedep) == 0) WORKLIST_INSERT(&bp->b_dep, &pagedep->pd_list); dap->da_pagedep = pagedep; LIST_INSERT_HEAD(&pagedep->pd_diraddhd[DIRADDHASH(offset)], dap, da_pdlist); /* * Link into its inodedep. Put it on the id_bufwait list if the inode * is not yet written. If it is written, do the post-inode write * processing to put it on the id_pendinghd list. */ (void) inodedep_lookup(fs, newinum, DEPALLOC, &inodedep); if ((inodedep->id_state & ALLCOMPLETE) == ALLCOMPLETE) diradd_inode_written(dap, inodedep); else WORKLIST_INSERT(&inodedep->id_bufwait, &dap->da_list); if (isnewblk) { /* * Directories growing into indirect blocks are rare * enough and the frequency of new block allocation * in those cases even more rare, that we choose not * to bother tracking them. Rather we simply force the * new directory entry to disk. */ if (lbn >= NDADDR) { FREE_LOCK(&lk); /* * We only have a new allocation when at the * beginning of a new block, not when we are * expanding into an existing block. */ if (blkoff(fs, diroffset) == 0) return (1); return (0); } /* * We only have a new allocation when at the beginning * of a new fragment, not when we are expanding into an * existing fragment. Also, there is nothing to do if we * are already tracking this block. */ if (fragoff(fs, diroffset) != 0) { FREE_LOCK(&lk); return (0); } if ((pagedep->pd_state & NEWBLOCK) != 0) { WORKITEM_FREE(newdirblk, D_NEWDIRBLK); FREE_LOCK(&lk); return (0); } /* * Find our associated allocdirect and have it track us. */ if (inodedep_lookup(fs, dp->i_number, 0, &inodedep) == 0) panic("softdep_setup_directory_add: lost inodedep"); adp = TAILQ_LAST(&inodedep->id_newinoupdt, allocdirectlst); if (adp == NULL || adp->ad_lbn != lbn) { FREE_LOCK(&lk); panic("softdep_setup_directory_add: lost entry"); } pagedep->pd_state |= NEWBLOCK; newdirblk->db_pagedep = pagedep; WORKLIST_INSERT(&adp->ad_newdirblk, &newdirblk->db_list); } FREE_LOCK(&lk); return (0); } /* * This procedure is called to change the offset of a directory * entry when compacting a directory block which must be owned * exclusively by the caller. Note that the actual entry movement * must be done in this procedure to ensure that no I/O completions * occur while the move is in progress. */ void softdep_change_directoryentry_offset(dp, base, oldloc, newloc, entrysize) struct inode *dp; /* inode for directory */ caddr_t base; /* address of dp->i_offset */ caddr_t oldloc; /* address of old directory location */ caddr_t newloc; /* address of new directory location */ int entrysize; /* size of directory entry */ { int offset, oldoffset, newoffset; struct pagedep *pagedep; struct diradd *dap; ufs_lbn_t lbn; ACQUIRE_LOCK(&lk); lbn = lblkno(dp->i_fs, dp->i_offset); offset = blkoff(dp->i_fs, dp->i_offset); if (pagedep_lookup(dp, lbn, 0, &pagedep) == 0) goto done; oldoffset = offset + (oldloc - base); newoffset = offset + (newloc - base); LIST_FOREACH(dap, &pagedep->pd_diraddhd[DIRADDHASH(oldoffset)], da_pdlist) { if (dap->da_offset != oldoffset) continue; dap->da_offset = newoffset; if (DIRADDHASH(newoffset) == DIRADDHASH(oldoffset)) break; LIST_REMOVE(dap, da_pdlist); LIST_INSERT_HEAD(&pagedep->pd_diraddhd[DIRADDHASH(newoffset)], dap, da_pdlist); break; } if (dap == NULL) { LIST_FOREACH(dap, &pagedep->pd_pendinghd, da_pdlist) { if (dap->da_offset == oldoffset) { dap->da_offset = newoffset; break; } } } done: bcopy(oldloc, newloc, entrysize); FREE_LOCK(&lk); } /* * Free a diradd dependency structure. This routine must be called * with splbio interrupts blocked. */ STATIC void free_diradd(dap) struct diradd *dap; { struct dirrem *dirrem; struct pagedep *pagedep; struct inodedep *inodedep; struct mkdir *mkdir, *nextmd; #ifdef DEBUG if (lk.lkt_held == -1) panic("free_diradd: lock not held"); #endif WORKLIST_REMOVE(&dap->da_list); LIST_REMOVE(dap, da_pdlist); if ((dap->da_state & DIRCHG) == 0) { pagedep = dap->da_pagedep; } else { dirrem = dap->da_previous; pagedep = dirrem->dm_pagedep; dirrem->dm_dirinum = pagedep->pd_ino; add_to_worklist(&dirrem->dm_list); } if (inodedep_lookup(VFSTOUFS(pagedep->pd_mnt)->um_fs, dap->da_newinum, 0, &inodedep) != 0) (void) free_inodedep(inodedep); if ((dap->da_state & (MKDIR_PARENT | MKDIR_BODY)) != 0) { for (mkdir = LIST_FIRST(&mkdirlisthd); mkdir; mkdir = nextmd) { nextmd = LIST_NEXT(mkdir, md_mkdirs); if (mkdir->md_diradd != dap) continue; dap->da_state &= ~mkdir->md_state; WORKLIST_REMOVE(&mkdir->md_list); LIST_REMOVE(mkdir, md_mkdirs); WORKITEM_FREE(mkdir, D_MKDIR); } if ((dap->da_state & (MKDIR_PARENT | MKDIR_BODY)) != 0) { FREE_LOCK(&lk); panic("free_diradd: unfound ref"); } } WORKITEM_FREE(dap, D_DIRADD); } /* * Directory entry removal dependencies. * * When removing a directory entry, the entry's inode pointer must be * zero'ed on disk before the corresponding inode's link count is decremented * (possibly freeing the inode for re-use). This dependency is handled by * updating the directory entry but delaying the inode count reduction until * after the directory block has been written to disk. After this point, the * inode count can be decremented whenever it is convenient. */ /* * This routine should be called immediately after removing * a directory entry. The inode's link count should not be * decremented by the calling procedure -- the soft updates * code will do this task when it is safe. */ void softdep_setup_remove(bp, dp, ip, isrmdir) struct buf *bp; /* buffer containing directory block */ struct inode *dp; /* inode for the directory being modified */ struct inode *ip; /* inode for directory entry being removed */ int isrmdir; /* indicates if doing RMDIR */ { struct dirrem *dirrem, *prevdirrem; /* * Allocate a new dirrem if appropriate and ACQUIRE_LOCK. */ dirrem = newdirrem(bp, dp, ip, isrmdir, &prevdirrem); /* * If the COMPLETE flag is clear, then there were no active * entries and we want to roll back to a zeroed entry until * the new inode is committed to disk. If the COMPLETE flag is * set then we have deleted an entry that never made it to * disk. If the entry we deleted resulted from a name change, * then the old name still resides on disk. We cannot delete * its inode (returned to us in prevdirrem) until the zeroed * directory entry gets to disk. The new inode has never been * referenced on the disk, so can be deleted immediately. */ if ((dirrem->dm_state & COMPLETE) == 0) { LIST_INSERT_HEAD(&dirrem->dm_pagedep->pd_dirremhd, dirrem, dm_next); FREE_LOCK(&lk); } else { if (prevdirrem != NULL) LIST_INSERT_HEAD(&dirrem->dm_pagedep->pd_dirremhd, prevdirrem, dm_next); dirrem->dm_dirinum = dirrem->dm_pagedep->pd_ino; FREE_LOCK(&lk); handle_workitem_remove(dirrem); } } /* * Allocate a new dirrem if appropriate and return it along with * its associated pagedep. Called without a lock, returns with lock. */ STATIC long num_dirrem; /* number of dirrem allocated */ STATIC struct dirrem * newdirrem(bp, dp, ip, isrmdir, prevdirremp) struct buf *bp; /* buffer containing directory block */ struct inode *dp; /* inode for the directory being modified */ struct inode *ip; /* inode for directory entry being removed */ int isrmdir; /* indicates if doing RMDIR */ struct dirrem **prevdirremp; /* previously referenced inode, if any */ { int offset; ufs_lbn_t lbn; struct diradd *dap; struct dirrem *dirrem; struct pagedep *pagedep; /* * Whiteouts have no deletion dependencies. */ if (ip == NULL) panic("newdirrem: whiteout"); /* * If we are over our limit, try to improve the situation. * Limiting the number of dirrem structures will also limit * the number of freefile and freeblks structures. */ if (num_dirrem > max_softdeps / 2) (void) request_cleanup(FLUSH_REMOVE, 0); num_dirrem += 1; dirrem = pool_get(&dirrem_pool, PR_WAITOK); bzero(dirrem,sizeof(struct dirrem)); dirrem->dm_list.wk_type = D_DIRREM; dirrem->dm_state = isrmdir ? RMDIR : 0; dirrem->dm_mnt = ITOV(ip)->v_mount; dirrem->dm_oldinum = ip->i_number; *prevdirremp = NULL; ACQUIRE_LOCK(&lk); lbn = lblkno(dp->i_fs, dp->i_offset); offset = blkoff(dp->i_fs, dp->i_offset); if (pagedep_lookup(dp, lbn, DEPALLOC, &pagedep) == 0) WORKLIST_INSERT(&bp->b_dep, &pagedep->pd_list); dirrem->dm_pagedep = pagedep; /* * Check for a diradd dependency for the same directory entry. * If present, then both dependencies become obsolete and can * be de-allocated. Check for an entry on both the pd_dirraddhd * list and the pd_pendinghd list. */ LIST_FOREACH(dap, &pagedep->pd_diraddhd[DIRADDHASH(offset)], da_pdlist) if (dap->da_offset == offset) break; if (dap == NULL) { LIST_FOREACH(dap, &pagedep->pd_pendinghd, da_pdlist) if (dap->da_offset == offset) break; if (dap == NULL) return (dirrem); } /* * Must be ATTACHED at this point. */ if ((dap->da_state & ATTACHED) == 0) { FREE_LOCK(&lk); panic("newdirrem: not ATTACHED"); } if (dap->da_newinum != ip->i_number) { FREE_LOCK(&lk); panic("newdirrem: inum %d should be %d", ip->i_number, dap->da_newinum); } /* * If we are deleting a changed name that never made it to disk, * then return the dirrem describing the previous inode (which * represents the inode currently referenced from this entry on disk). */ if ((dap->da_state & DIRCHG) != 0) { *prevdirremp = dap->da_previous; dap->da_state &= ~DIRCHG; dap->da_pagedep = pagedep; } /* * We are deleting an entry that never made it to disk. * Mark it COMPLETE so we can delete its inode immediately. */ dirrem->dm_state |= COMPLETE; free_diradd(dap); return (dirrem); } /* * Directory entry change dependencies. * * Changing an existing directory entry requires that an add operation * be completed first followed by a deletion. The semantics for the addition * are identical to the description of adding a new entry above except * that the rollback is to the old inode number rather than zero. Once * the addition dependency is completed, the removal is done as described * in the removal routine above. */ /* * This routine should be called immediately after changing * a directory entry. The inode's link count should not be * decremented by the calling procedure -- the soft updates * code will perform this task when it is safe. */ void softdep_setup_directory_change(bp, dp, ip, newinum, isrmdir) struct buf *bp; /* buffer containing directory block */ struct inode *dp; /* inode for the directory being modified */ struct inode *ip; /* inode for directory entry being removed */ long newinum; /* new inode number for changed entry */ int isrmdir; /* indicates if doing RMDIR */ { int offset; struct diradd *dap = NULL; struct dirrem *dirrem, *prevdirrem; struct pagedep *pagedep; struct inodedep *inodedep; offset = blkoff(dp->i_fs, dp->i_offset); dap = pool_get(&diradd_pool, PR_WAITOK); bzero(dap,sizeof(struct diradd)); dap->da_list.wk_type = D_DIRADD; dap->da_state = DIRCHG | ATTACHED | DEPCOMPLETE; dap->da_offset = offset; dap->da_newinum = newinum; /* * Allocate a new dirrem and ACQUIRE_LOCK. */ dirrem = newdirrem(bp, dp, ip, isrmdir, &prevdirrem); pagedep = dirrem->dm_pagedep; /* * The possible values for isrmdir: * 0 - non-directory file rename * 1 - directory rename within same directory * inum - directory rename to new directory of given inode number * When renaming to a new directory, we are both deleting and * creating a new directory entry, so the link count on the new * directory should not change. Thus we do not need the followup * dirrem which is usually done in handle_workitem_remove. We set * the DIRCHG flag to tell handle_workitem_remove to skip the * followup dirrem. */ if (isrmdir > 1) dirrem->dm_state |= DIRCHG; /* * If the COMPLETE flag is clear, then there were no active * entries and we want to roll back to the previous inode until * the new inode is committed to disk. If the COMPLETE flag is * set, then we have deleted an entry that never made it to disk. * If the entry we deleted resulted from a name change, then the old * inode reference still resides on disk. Any rollback that we do * needs to be to that old inode (returned to us in prevdirrem). If * the entry we deleted resulted from a create, then there is * no entry on the disk, so we want to roll back to zero rather * than the uncommitted inode. In either of the COMPLETE cases we * want to immediately free the unwritten and unreferenced inode. */ if ((dirrem->dm_state & COMPLETE) == 0) { dap->da_previous = dirrem; } else { if (prevdirrem != NULL) { dap->da_previous = prevdirrem; } else { dap->da_state &= ~DIRCHG; dap->da_pagedep = pagedep; } dirrem->dm_dirinum = pagedep->pd_ino; add_to_worklist(&dirrem->dm_list); } /* * Link into its inodedep. Put it on the id_bufwait list if the inode * is not yet written. If it is written, do the post-inode write * processing to put it on the id_pendinghd list. */ if (inodedep_lookup(dp->i_fs, newinum, DEPALLOC, &inodedep) == 0 || (inodedep->id_state & ALLCOMPLETE) == ALLCOMPLETE) { dap->da_state |= COMPLETE; LIST_INSERT_HEAD(&pagedep->pd_pendinghd, dap, da_pdlist); WORKLIST_INSERT(&inodedep->id_pendinghd, &dap->da_list); } else { LIST_INSERT_HEAD(&pagedep->pd_diraddhd[DIRADDHASH(offset)], dap, da_pdlist); WORKLIST_INSERT(&inodedep->id_bufwait, &dap->da_list); } FREE_LOCK(&lk); } /* * Called whenever the link count on an inode is changed. * It creates an inode dependency so that the new reference(s) * to the inode cannot be committed to disk until the updated * inode has been written. */ void softdep_change_linkcnt(ip, nodelay) struct inode *ip; /* the inode with the increased link count */ int nodelay; /* do background work or not */ { struct inodedep *inodedep; int flags; /* * If requested, do not allow background work to happen. */ flags = DEPALLOC; if (nodelay) flags |= NODELAY; ACQUIRE_LOCK(&lk); (void) inodedep_lookup(ip->i_fs, ip->i_number, flags, &inodedep); if (ip->i_ffs_nlink < ip->i_effnlink) { FREE_LOCK(&lk); panic("softdep_change_linkcnt: bad delta"); } inodedep->id_nlinkdelta = ip->i_ffs_nlink - ip->i_effnlink; FREE_LOCK(&lk); } /* * This workitem decrements the inode's link count. * If the link count reaches zero, the file is removed. */ STATIC void handle_workitem_remove(dirrem) struct dirrem *dirrem; { struct proc *p = CURPROC; /* XXX */ struct inodedep *inodedep; struct vnode *vp; struct inode *ip; ino_t oldinum; int error; if ((error = VFS_VGET(dirrem->dm_mnt, dirrem->dm_oldinum, &vp)) != 0) { softdep_error("handle_workitem_remove: vget", error); return; } ip = VTOI(vp); ACQUIRE_LOCK(&lk); if ((inodedep_lookup(ip->i_fs, dirrem->dm_oldinum, 0, &inodedep)) == 0) { FREE_LOCK(&lk); panic("handle_workitem_remove: lost inodedep"); } /* * Normal file deletion. */ if ((dirrem->dm_state & RMDIR) == 0) { ip->i_ffs_nlink--; ip->i_flag |= IN_CHANGE; if (ip->i_ffs_nlink < ip->i_effnlink) { FREE_LOCK(&lk); panic("handle_workitem_remove: bad file delta"); } inodedep->id_nlinkdelta = ip->i_ffs_nlink - ip->i_effnlink; FREE_LOCK(&lk); vput(vp); num_dirrem -= 1; WORKITEM_FREE(dirrem, D_DIRREM); return; } /* * Directory deletion. Decrement reference count for both the * just deleted parent directory entry and the reference for ".". * Next truncate the directory to length zero. When the * truncation completes, arrange to have the reference count on * the parent decremented to account for the loss of "..". */ ip->i_ffs_nlink -= 2; ip->i_flag |= IN_CHANGE; if (ip->i_ffs_nlink < ip->i_effnlink) panic("handle_workitem_remove: bad dir delta"); inodedep->id_nlinkdelta = ip->i_ffs_nlink - ip->i_effnlink; FREE_LOCK(&lk); if ((error = UFS_TRUNCATE(ip, (off_t)0, 0, p->p_ucred)) != 0) softdep_error("handle_workitem_remove: truncate", error); /* * Rename a directory to a new parent. Since, we are both deleting * and creating a new directory entry, the link count on the new * directory should not change. Thus we skip the followup dirrem. */ if (dirrem->dm_state & DIRCHG) { vput(vp); num_dirrem -= 1; WORKITEM_FREE(dirrem, D_DIRREM); return; } /* * If the inodedep does not exist, then the zero'ed inode has * been written to disk. If the allocated inode has never been * written to disk, then the on-disk inode is zero'ed. In either * case we can remove the file immediately. */ ACQUIRE_LOCK(&lk); dirrem->dm_state = 0; oldinum = dirrem->dm_oldinum; dirrem->dm_oldinum = dirrem->dm_dirinum; if (inodedep_lookup(ip->i_fs, oldinum, 0, &inodedep) == 0 || check_inode_unwritten(inodedep)) { FREE_LOCK(&lk); vput(vp); handle_workitem_remove(dirrem); return; } WORKLIST_INSERT(&inodedep->id_inowait, &dirrem->dm_list); FREE_LOCK(&lk); ip->i_flag |= IN_CHANGE; UFS_UPDATE(VTOI(vp), 0); vput(vp); } /* * Inode de-allocation dependencies. * * When an inode's link count is reduced to zero, it can be de-allocated. We * found it convenient to postpone de-allocation until after the inode is * written to disk with its new link count (zero). At this point, all of the * on-disk inode's block pointers are nullified and, with careful dependency * list ordering, all dependencies related to the inode will be satisfied and * the corresponding dependency structures de-allocated. So, if/when the * inode is reused, there will be no mixing of old dependencies with new * ones. This artificial dependency is set up by the block de-allocation * procedure above (softdep_setup_freeblocks) and completed by the * following procedure. */ STATIC void handle_workitem_freefile(freefile) struct freefile *freefile; { struct fs *fs; struct vnode vp; struct inode tip; #ifdef DEBUG struct inodedep *idp; #endif int error; fs = VFSTOUFS(freefile->fx_mnt)->um_fs; #ifdef DEBUG ACQUIRE_LOCK(&lk); error = inodedep_lookup(fs, freefile->fx_oldinum, 0, &idp); FREE_LOCK(&lk); if (error) panic("handle_workitem_freefile: inodedep survived"); #endif tip.i_ump = VFSTOUFS(freefile->fx_mnt); tip.i_dev = freefile->fx_devvp->v_rdev; tip.i_fs = fs; tip.i_vnode = &vp; vp.v_data = &tip; if ((error = ffs_freefile(&tip, freefile->fx_oldinum, freefile->fx_mode)) != 0) { softdep_error("handle_workitem_freefile", error); } WORKITEM_FREE(freefile, D_FREEFILE); } /* * Disk writes. * * The dependency structures constructed above are most actively used when file * system blocks are written to disk. No constraints are placed on when a * block can be written, but unsatisfied update dependencies are made safe by * modifying (or replacing) the source memory for the duration of the disk * write. When the disk write completes, the memory block is again brought * up-to-date. * * In-core inode structure reclamation. * * Because there are a finite number of "in-core" inode structures, they are * reused regularly. By transferring all inode-related dependencies to the * in-memory inode block and indexing them separately (via "inodedep"s), we * can allow "in-core" inode structures to be reused at any time and avoid * any increase in contention. * * Called just before entering the device driver to initiate a new disk I/O. * The buffer must be locked, thus, no I/O completion operations can occur * while we are manipulating its associated dependencies. */ void softdep_disk_io_initiation(bp) struct buf *bp; /* structure describing disk write to occur */ { struct worklist *wk, *nextwk; struct indirdep *indirdep; /* * We only care about write operations. There should never * be dependencies for reads. */ if (bp->b_flags & B_READ) panic("softdep_disk_io_initiation: read"); /* * Do any necessary pre-I/O processing. */ for (wk = LIST_FIRST(&bp->b_dep); wk; wk = nextwk) { nextwk = LIST_NEXT(wk, wk_list); switch (wk->wk_type) { case D_PAGEDEP: initiate_write_filepage(WK_PAGEDEP(wk), bp); continue; case D_INODEDEP: initiate_write_inodeblock(WK_INODEDEP(wk), bp); continue; case D_INDIRDEP: indirdep = WK_INDIRDEP(wk); if (indirdep->ir_state & GOINGAWAY) panic("disk_io_initiation: indirdep gone"); /* * If there are no remaining dependencies, this * will be writing the real pointers, so the * dependency can be freed. */ if (LIST_FIRST(&indirdep->ir_deplisthd) == NULL) { indirdep->ir_savebp->b_flags |= B_INVAL | B_NOCACHE; brelse(indirdep->ir_savebp); /* inline expand WORKLIST_REMOVE(wk); */ wk->wk_state &= ~ONWORKLIST; LIST_REMOVE(wk, wk_list); WORKITEM_FREE(indirdep, D_INDIRDEP); continue; } /* * Replace up-to-date version with safe version. */ indirdep->ir_saveddata = malloc(bp->b_bcount, M_INDIRDEP, M_WAITOK); ACQUIRE_LOCK(&lk); indirdep->ir_state &= ~ATTACHED; indirdep->ir_state |= UNDONE; bcopy(bp->b_data, indirdep->ir_saveddata, bp->b_bcount); bcopy(indirdep->ir_savebp->b_data, bp->b_data, bp->b_bcount); FREE_LOCK(&lk); continue; case D_MKDIR: case D_BMSAFEMAP: case D_ALLOCDIRECT: case D_ALLOCINDIR: continue; default: panic("handle_disk_io_initiation: Unexpected type %s", TYPENAME(wk->wk_type)); /* NOTREACHED */ } } } /* * Called from within the procedure above to deal with unsatisfied * allocation dependencies in a directory. The buffer must be locked, * thus, no I/O completion operations can occur while we are * manipulating its associated dependencies. */ STATIC void initiate_write_filepage(pagedep, bp) struct pagedep *pagedep; struct buf *bp; { struct diradd *dap; struct direct *ep; int i; if (pagedep->pd_state & IOSTARTED) { /* * This can only happen if there is a driver that does not * understand chaining. Here biodone will reissue the call * to strategy for the incomplete buffers. */ printf("initiate_write_filepage: already started\n"); return; } pagedep->pd_state |= IOSTARTED; ACQUIRE_LOCK(&lk); for (i = 0; i < DAHASHSZ; i++) { LIST_FOREACH(dap, &pagedep->pd_diraddhd[i], da_pdlist) { ep = (struct direct *) ((char *)bp->b_data + dap->da_offset); if (ep->d_ino != dap->da_newinum) { FREE_LOCK(&lk); panic("%s: dir inum %d != new %d", "initiate_write_filepage", ep->d_ino, dap->da_newinum); } if (dap->da_state & DIRCHG) ep->d_ino = dap->da_previous->dm_oldinum; else ep->d_ino = 0; dap->da_state &= ~ATTACHED; dap->da_state |= UNDONE; } } FREE_LOCK(&lk); } /* * Called from within the procedure above to deal with unsatisfied * allocation dependencies in an inodeblock. The buffer must be * locked, thus, no I/O completion operations can occur while we * are manipulating its associated dependencies. */ STATIC void initiate_write_inodeblock(inodedep, bp) struct inodedep *inodedep; struct buf *bp; /* The inode block */ { struct allocdirect *adp, *lastadp; struct ufs1_dinode *dp; struct fs *fs; #ifdef DIAGNOSTIC ufs_lbn_t prevlbn = 0; #endif int i, deplist; if (inodedep->id_state & IOSTARTED) panic("initiate_write_inodeblock: already started"); inodedep->id_state |= IOSTARTED; fs = inodedep->id_fs; dp = (struct ufs1_dinode *)bp->b_data + ino_to_fsbo(fs, inodedep->id_ino); /* * If the bitmap is not yet written, then the allocated * inode cannot be written to disk. */ if ((inodedep->id_state & DEPCOMPLETE) == 0) { if (inodedep->id_savedino != NULL) panic("initiate_write_inodeblock: already doing I/O"); MALLOC(inodedep->id_savedino, struct ufs1_dinode *, sizeof(struct ufs1_dinode), M_INODEDEP, M_WAITOK); *inodedep->id_savedino = *dp; bzero((caddr_t)dp, sizeof(struct ufs1_dinode)); return; } /* * If no dependencies, then there is nothing to roll back. */ inodedep->id_savedsize = dp->di_size; if (TAILQ_FIRST(&inodedep->id_inoupdt) == NULL) return; /* * Set the dependencies to busy. */ ACQUIRE_LOCK(&lk); for (deplist = 0, adp = TAILQ_FIRST(&inodedep->id_inoupdt); adp; adp = TAILQ_NEXT(adp, ad_next)) { #ifdef DIAGNOSTIC if (deplist != 0 && prevlbn >= adp->ad_lbn) { FREE_LOCK(&lk); panic("softdep_write_inodeblock: lbn order"); } prevlbn = adp->ad_lbn; if (adp->ad_lbn < NDADDR && dp->di_db[adp->ad_lbn] != adp->ad_newblkno) { FREE_LOCK(&lk); panic("%s: direct pointer #%ld mismatch %d != %d", "softdep_write_inodeblock", (long)(adp->ad_lbn), dp->di_db[adp->ad_lbn], adp->ad_newblkno); } if (adp->ad_lbn >= NDADDR && dp->di_ib[adp->ad_lbn - NDADDR] != adp->ad_newblkno) { FREE_LOCK(&lk); panic("%s: indirect pointer #%ld mismatch %d != %d", "softdep_write_inodeblock", (long)(adp->ad_lbn - NDADDR), dp->di_ib[adp->ad_lbn - NDADDR], adp->ad_newblkno); } deplist |= 1 << adp->ad_lbn; if ((adp->ad_state & ATTACHED) == 0) { FREE_LOCK(&lk); panic("softdep_write_inodeblock: Unknown state 0x%x", adp->ad_state); } #endif /* DIAGNOSTIC */ adp->ad_state &= ~ATTACHED; adp->ad_state |= UNDONE; } /* * The on-disk inode cannot claim to be any larger than the last * fragment that has been written. Otherwise, the on-disk inode * might have fragments that were not the last block in the file * which would corrupt the filesystem. */ for (lastadp = NULL, adp = TAILQ_FIRST(&inodedep->id_inoupdt); adp; lastadp = adp, adp = TAILQ_NEXT(adp, ad_next)) { if (adp->ad_lbn >= NDADDR) break; dp->di_db[adp->ad_lbn] = adp->ad_oldblkno; /* keep going until hitting a rollback to a frag */ if (adp->ad_oldsize == 0 || adp->ad_oldsize == fs->fs_bsize) continue; dp->di_size = fs->fs_bsize * adp->ad_lbn + adp->ad_oldsize; for (i = adp->ad_lbn + 1; i < NDADDR; i++) { #ifdef DIAGNOSTIC if (dp->di_db[i] != 0 && (deplist & (1 << i)) == 0) { FREE_LOCK(&lk); panic("softdep_write_inodeblock: lost dep1"); } #endif /* DIAGNOSTIC */ dp->di_db[i] = 0; } for (i = 0; i < NIADDR; i++) { #ifdef DIAGNOSTIC if (dp->di_ib[i] != 0 && (deplist & ((1 << NDADDR) << i)) == 0) { FREE_LOCK(&lk); panic("softdep_write_inodeblock: lost dep2"); } #endif /* DIAGNOSTIC */ dp->di_ib[i] = 0; } FREE_LOCK(&lk); return; } /* * If we have zero'ed out the last allocated block of the file, * roll back the size to the last currently allocated block. * We know that this last allocated block is a full-sized as * we already checked for fragments in the loop above. */ if (lastadp != NULL && dp->di_size <= (lastadp->ad_lbn + 1) * fs->fs_bsize) { for (i = lastadp->ad_lbn; i >= 0; i--) if (dp->di_db[i] != 0) break; dp->di_size = (i + 1) * fs->fs_bsize; } /* * The only dependencies are for indirect blocks. * * The file size for indirect block additions is not guaranteed. * Such a guarantee would be non-trivial to achieve. The conventional * synchronous write implementation also does not make this guarantee. * Fsck should catch and fix discrepancies. Arguably, the file size * can be over-estimated without destroying integrity when the file * moves into the indirect blocks (i.e., is large). If we want to * postpone fsck, we are stuck with this argument. */ for (; adp; adp = TAILQ_NEXT(adp, ad_next)) dp->di_ib[adp->ad_lbn - NDADDR] = 0; FREE_LOCK(&lk); } /* * This routine is called during the completion interrupt * service routine for a disk write (from the procedure called * by the device driver to inform the filesystem caches of * a request completion). It should be called early in this * procedure, before the block is made available to other * processes or other routines are called. */ void softdep_disk_write_complete(bp) struct buf *bp; /* describes the completed disk write */ { struct worklist *wk; struct workhead reattach; struct newblk *newblk; struct allocindir *aip; struct allocdirect *adp; struct indirdep *indirdep; struct inodedep *inodedep; struct bmsafemap *bmsafemap; /* * If an error occurred while doing the write, then the data * has not hit the disk and the dependencies cannot be unrolled. */ if ((bp->b_flags & B_ERROR) && !(bp->b_flags & B_INVAL)) return; #ifdef DEBUG if (lk.lkt_held != -1) panic("softdep_disk_write_complete: lock is held"); lk.lkt_held = -2; #endif LIST_INIT(&reattach); while ((wk = LIST_FIRST(&bp->b_dep)) != NULL) { WORKLIST_REMOVE(wk); switch (wk->wk_type) { case D_PAGEDEP: if (handle_written_filepage(WK_PAGEDEP(wk), bp)) WORKLIST_INSERT(&reattach, wk); continue; case D_INODEDEP: if (handle_written_inodeblock(WK_INODEDEP(wk), bp)) WORKLIST_INSERT(&reattach, wk); continue; case D_BMSAFEMAP: bmsafemap = WK_BMSAFEMAP(wk); while ((newblk = LIST_FIRST(&bmsafemap->sm_newblkhd))) { newblk->nb_state |= DEPCOMPLETE; newblk->nb_bmsafemap = NULL; LIST_REMOVE(newblk, nb_deps); } while ((adp = LIST_FIRST(&bmsafemap->sm_allocdirecthd))) { adp->ad_state |= DEPCOMPLETE; adp->ad_buf = NULL; LIST_REMOVE(adp, ad_deps); handle_allocdirect_partdone(adp); } while ((aip = LIST_FIRST(&bmsafemap->sm_allocindirhd))) { aip->ai_state |= DEPCOMPLETE; aip->ai_buf = NULL; LIST_REMOVE(aip, ai_deps); handle_allocindir_partdone(aip); } while ((inodedep = LIST_FIRST(&bmsafemap->sm_inodedephd)) != NULL) { inodedep->id_state |= DEPCOMPLETE; LIST_REMOVE(inodedep, id_deps); inodedep->id_buf = NULL; } WORKITEM_FREE(bmsafemap, D_BMSAFEMAP); continue; case D_MKDIR: handle_written_mkdir(WK_MKDIR(wk), MKDIR_BODY); continue; case D_ALLOCDIRECT: adp = WK_ALLOCDIRECT(wk); adp->ad_state |= COMPLETE; handle_allocdirect_partdone(adp); continue; case D_ALLOCINDIR: aip = WK_ALLOCINDIR(wk); aip->ai_state |= COMPLETE; handle_allocindir_partdone(aip); continue; case D_INDIRDEP: indirdep = WK_INDIRDEP(wk); if (indirdep->ir_state & GOINGAWAY) panic("disk_write_complete: indirdep gone"); bcopy(indirdep->ir_saveddata, bp->b_data, bp->b_bcount); free(indirdep->ir_saveddata, M_INDIRDEP); indirdep->ir_saveddata = 0; indirdep->ir_state &= ~UNDONE; indirdep->ir_state |= ATTACHED; while ((aip = LIST_FIRST(&indirdep->ir_donehd)) != 0) { handle_allocindir_partdone(aip); if (aip == LIST_FIRST(&indirdep->ir_donehd)) panic("disk_write_complete: not gone"); } WORKLIST_INSERT(&reattach, wk); if ((bp->b_flags & B_DELWRI) == 0) stat_indir_blk_ptrs++; buf_dirty(bp); continue; default: panic("handle_disk_write_complete: Unknown type %s", TYPENAME(wk->wk_type)); /* NOTREACHED */ } } /* * Reattach any requests that must be redone. */ while ((wk = LIST_FIRST(&reattach)) != NULL) { WORKLIST_REMOVE(wk); WORKLIST_INSERT(&bp->b_dep, wk); } #ifdef DEBUG if (lk.lkt_held != -2) panic("softdep_disk_write_complete: lock lost"); lk.lkt_held = -1; #endif } /* * Called from within softdep_disk_write_complete above. Note that * this routine is always called from interrupt level with further * splbio interrupts blocked. */ STATIC void handle_allocdirect_partdone(adp) struct allocdirect *adp; /* the completed allocdirect */ { struct allocdirect *listadp; struct inodedep *inodedep; long bsize, delay; if ((adp->ad_state & ALLCOMPLETE) != ALLCOMPLETE) return; if (adp->ad_buf != NULL) panic("handle_allocdirect_partdone: dangling dep"); /* * The on-disk inode cannot claim to be any larger than the last * fragment that has been written. Otherwise, the on-disk inode * might have fragments that were not the last block in the file * which would corrupt the filesystem. Thus, we cannot free any * allocdirects after one whose ad_oldblkno claims a fragment as * these blocks must be rolled back to zero before writing the inode. * We check the currently active set of allocdirects in id_inoupdt. */ inodedep = adp->ad_inodedep; bsize = inodedep->id_fs->fs_bsize; TAILQ_FOREACH(listadp, &inodedep->id_inoupdt, ad_next) { /* found our block */ if (listadp == adp) break; /* continue if ad_oldlbn is not a fragment */ if (listadp->ad_oldsize == 0 || listadp->ad_oldsize == bsize) continue; /* hit a fragment */ return; } /* * If we have reached the end of the current list without * finding the just finished dependency, then it must be * on the future dependency list. Future dependencies cannot * be freed until they are moved to the current list. */ if (listadp == NULL) { #ifdef DEBUG TAILQ_FOREACH(listadp, &inodedep->id_newinoupdt, ad_next) /* found our block */ if (listadp == adp) break; if (listadp == NULL) panic("handle_allocdirect_partdone: lost dep"); #endif /* DEBUG */ return; } /* * If we have found the just finished dependency, then free * it along with anything that follows it that is complete. * If the inode still has a bitmap dependency, then it has * never been written to disk, hence the on-disk inode cannot * reference the old fragment so we can free it without delay. */ delay = (inodedep->id_state & DEPCOMPLETE); for (; adp; adp = listadp) { listadp = TAILQ_NEXT(adp, ad_next); if ((adp->ad_state & ALLCOMPLETE) != ALLCOMPLETE) return; free_allocdirect(&inodedep->id_inoupdt, adp, delay); } } /* * Called from within softdep_disk_write_complete above. Note that * this routine is always called from interrupt level with further * splbio interrupts blocked. */ STATIC void handle_allocindir_partdone(aip) struct allocindir *aip; /* the completed allocindir */ { struct indirdep *indirdep; if ((aip->ai_state & ALLCOMPLETE) != ALLCOMPLETE) return; if (aip->ai_buf != NULL) panic("handle_allocindir_partdone: dangling dependency"); indirdep = aip->ai_indirdep; if (indirdep->ir_state & UNDONE) { LIST_REMOVE(aip, ai_next); LIST_INSERT_HEAD(&indirdep->ir_donehd, aip, ai_next); return; } ((daddr_t *)indirdep->ir_savebp->b_data)[aip->ai_offset] = aip->ai_newblkno; LIST_REMOVE(aip, ai_next); if (aip->ai_freefrag != NULL) add_to_worklist(&aip->ai_freefrag->ff_list); WORKITEM_FREE(aip, D_ALLOCINDIR); } /* * Called from within softdep_disk_write_complete above to restore * in-memory inode block contents to their most up-to-date state. Note * that this routine is always called from interrupt level with further * splbio interrupts blocked. */ STATIC int handle_written_inodeblock(inodedep, bp) struct inodedep *inodedep; struct buf *bp; /* buffer containing the inode block */ { struct worklist *wk, *filefree; struct allocdirect *adp, *nextadp; struct ufs1_dinode *dp; int hadchanges; if ((inodedep->id_state & IOSTARTED) == 0) panic("handle_written_inodeblock: not started"); inodedep->id_state &= ~IOSTARTED; dp = (struct ufs1_dinode *)bp->b_data + ino_to_fsbo(inodedep->id_fs, inodedep->id_ino); /* * If we had to rollback the inode allocation because of * bitmaps being incomplete, then simply restore it. * Keep the block dirty so that it will not be reclaimed until * all associated dependencies have been cleared and the * corresponding updates written to disk. */ if (inodedep->id_savedino != NULL) { *dp = *inodedep->id_savedino; FREE(inodedep->id_savedino, M_INODEDEP); inodedep->id_savedino = NULL; if ((bp->b_flags & B_DELWRI) == 0) stat_inode_bitmap++; buf_dirty(bp); return (1); } inodedep->id_state |= COMPLETE; /* * Roll forward anything that had to be rolled back before * the inode could be updated. */ hadchanges = 0; for (adp = TAILQ_FIRST(&inodedep->id_inoupdt); adp; adp = nextadp) { nextadp = TAILQ_NEXT(adp, ad_next); if (adp->ad_state & ATTACHED) panic("handle_written_inodeblock: new entry"); if (adp->ad_lbn < NDADDR) { if (dp->di_db[adp->ad_lbn] != adp->ad_oldblkno) panic("%s: %s #%ld mismatch %d != %d", "handle_written_inodeblock", "direct pointer", (long)(adp->ad_lbn), dp->di_db[adp->ad_lbn], adp->ad_oldblkno); dp->di_db[adp->ad_lbn] = adp->ad_newblkno; } else { if (dp->di_ib[adp->ad_lbn - NDADDR] != 0) panic("%s: %s #%ld allocated as %d", "handle_written_inodeblock", "indirect pointer", (long)(adp->ad_lbn - NDADDR), dp->di_ib[adp->ad_lbn - NDADDR]); dp->di_ib[adp->ad_lbn - NDADDR] = adp->ad_newblkno; } adp->ad_state &= ~UNDONE; adp->ad_state |= ATTACHED; hadchanges = 1; } if (hadchanges && (bp->b_flags & B_DELWRI) == 0) stat_direct_blk_ptrs++; /* * Reset the file size to its most up-to-date value. */ if (inodedep->id_savedsize == -1) panic("handle_written_inodeblock: bad size"); if (dp->di_size != inodedep->id_savedsize) { dp->di_size = inodedep->id_savedsize; hadchanges = 1; } inodedep->id_savedsize = -1; /* * If there were any rollbacks in the inode block, then it must be * marked dirty so that its will eventually get written back in * its correct form. */ if (hadchanges) buf_dirty(bp); /* * Process any allocdirects that completed during the update. */ if ((adp = TAILQ_FIRST(&inodedep->id_inoupdt)) != NULL) handle_allocdirect_partdone(adp); /* * Process deallocations that were held pending until the * inode had been written to disk. Freeing of the inode * is delayed until after all blocks have been freed to * avoid creation of new triples * before the old ones have been deleted. */ filefree = NULL; while ((wk = LIST_FIRST(&inodedep->id_bufwait)) != NULL) { WORKLIST_REMOVE(wk); switch (wk->wk_type) { case D_FREEFILE: /* * We defer adding filefree to the worklist until * all other additions have been made to ensure * that it will be done after all the old blocks * have been freed. */ if (filefree != NULL) panic("handle_written_inodeblock: filefree"); filefree = wk; continue; case D_MKDIR: handle_written_mkdir(WK_MKDIR(wk), MKDIR_PARENT); continue; case D_DIRADD: diradd_inode_written(WK_DIRADD(wk), inodedep); continue; case D_FREEBLKS: wk->wk_state |= COMPLETE; if ((wk->wk_state & ALLCOMPLETE) != ALLCOMPLETE) continue; /* FALLTHROUGH */ case D_FREEFRAG: case D_DIRREM: add_to_worklist(wk); continue; case D_NEWDIRBLK: free_newdirblk(WK_NEWDIRBLK(wk)); continue; default: panic("handle_written_inodeblock: Unknown type %s", TYPENAME(wk->wk_type)); /* NOTREACHED */ } } if (filefree != NULL) { if (free_inodedep(inodedep) == 0) panic("handle_written_inodeblock: live inodedep"); add_to_worklist(filefree); return (0); } /* * If no outstanding dependencies, free it. */ if (free_inodedep(inodedep) || TAILQ_FIRST(&inodedep->id_inoupdt) == 0) return (0); return (hadchanges); } /* * Process a diradd entry after its dependent inode has been written. * This routine must be called with splbio interrupts blocked. */ STATIC void diradd_inode_written(dap, inodedep) struct diradd *dap; struct inodedep *inodedep; { struct pagedep *pagedep; dap->da_state |= COMPLETE; if ((dap->da_state & ALLCOMPLETE) == ALLCOMPLETE) { if (dap->da_state & DIRCHG) pagedep = dap->da_previous->dm_pagedep; else pagedep = dap->da_pagedep; LIST_REMOVE(dap, da_pdlist); LIST_INSERT_HEAD(&pagedep->pd_pendinghd, dap, da_pdlist); } WORKLIST_INSERT(&inodedep->id_pendinghd, &dap->da_list); } /* * Handle the completion of a mkdir dependency. */ STATIC void handle_written_mkdir(mkdir, type) struct mkdir *mkdir; int type; { struct diradd *dap; struct pagedep *pagedep; if (mkdir->md_state != type) panic("handle_written_mkdir: bad type"); dap = mkdir->md_diradd; dap->da_state &= ~type; if ((dap->da_state & (MKDIR_PARENT | MKDIR_BODY)) == 0) dap->da_state |= DEPCOMPLETE; if ((dap->da_state & ALLCOMPLETE) == ALLCOMPLETE) { if (dap->da_state & DIRCHG) pagedep = dap->da_previous->dm_pagedep; else pagedep = dap->da_pagedep; LIST_REMOVE(dap, da_pdlist); LIST_INSERT_HEAD(&pagedep->pd_pendinghd, dap, da_pdlist); } LIST_REMOVE(mkdir, md_mkdirs); WORKITEM_FREE(mkdir, D_MKDIR); } /* * Called from within softdep_disk_write_complete above. * A write operation was just completed. Removed inodes can * now be freed and associated block pointers may be committed. * Note that this routine is always called from interrupt level * with further splbio interrupts blocked. */ STATIC int handle_written_filepage(pagedep, bp) struct pagedep *pagedep; struct buf *bp; /* buffer containing the written page */ { struct dirrem *dirrem; struct diradd *dap, *nextdap; struct direct *ep; int i, chgs; if ((pagedep->pd_state & IOSTARTED) == 0) panic("handle_written_filepage: not started"); pagedep->pd_state &= ~IOSTARTED; /* * Process any directory removals that have been committed. */ while ((dirrem = LIST_FIRST(&pagedep->pd_dirremhd)) != NULL) { LIST_REMOVE(dirrem, dm_next); dirrem->dm_dirinum = pagedep->pd_ino; add_to_worklist(&dirrem->dm_list); } /* * Free any directory additions that have been committed. * If it is a newly allocated block, we have to wait until * the on-disk directory inode claims the new block. */ if ((pagedep->pd_state & NEWBLOCK) == 0) while ((dap = LIST_FIRST(&pagedep->pd_pendinghd)) != NULL) free_diradd(dap); /* * Uncommitted directory entries must be restored. */ for (chgs = 0, i = 0; i < DAHASHSZ; i++) { for (dap = LIST_FIRST(&pagedep->pd_diraddhd[i]); dap; dap = nextdap) { nextdap = LIST_NEXT(dap, da_pdlist); if (dap->da_state & ATTACHED) panic("handle_written_filepage: attached"); ep = (struct direct *) ((char *)bp->b_data + dap->da_offset); ep->d_ino = dap->da_newinum; dap->da_state &= ~UNDONE; dap->da_state |= ATTACHED; chgs = 1; /* * If the inode referenced by the directory has * been written out, then the dependency can be * moved to the pending list. */ if ((dap->da_state & ALLCOMPLETE) == ALLCOMPLETE) { LIST_REMOVE(dap, da_pdlist); LIST_INSERT_HEAD(&pagedep->pd_pendinghd, dap, da_pdlist); } } } /* * If there were any rollbacks in the directory, then it must be * marked dirty so that its will eventually get written back in * its correct form. */ if (chgs) { if ((bp->b_flags & B_DELWRI) == 0) stat_dir_entry++; buf_dirty(bp); return (1); } /* * If we are not waiting for a new directory block to be * claimed by its inode, then the pagedep will be freed. * Otherwise it will remain to track any new entries on * the page in case they are fsync'ed. */ if ((pagedep->pd_state & NEWBLOCK) == 0) { LIST_REMOVE(pagedep, pd_hash); WORKITEM_FREE(pagedep, D_PAGEDEP); } return (0); } /* * Writing back in-core inode structures. * * The filesystem only accesses an inode's contents when it occupies an * "in-core" inode structure. These "in-core" structures are separate from * the page frames used to cache inode blocks. Only the latter are * transferred to/from the disk. So, when the updated contents of the * "in-core" inode structure are copied to the corresponding in-memory inode * block, the dependencies are also transferred. The following procedure is * called when copying a dirty "in-core" inode to a cached inode block. */ /* * Called when an inode is loaded from disk. If the effective link count * differed from the actual link count when it was last flushed, then we * need to ensure that the correct effective link count is put back. */ void softdep_load_inodeblock(ip) struct inode *ip; /* the "in_core" copy of the inode */ { struct inodedep *inodedep; /* * Check for alternate nlink count. */ ip->i_effnlink = ip->i_ffs_nlink; ACQUIRE_LOCK(&lk); if (inodedep_lookup(ip->i_fs, ip->i_number, 0, &inodedep) == 0) { FREE_LOCK(&lk); return; } ip->i_effnlink -= inodedep->id_nlinkdelta; FREE_LOCK(&lk); } /* * This routine is called just before the "in-core" inode * information is to be copied to the in-memory inode block. * Recall that an inode block contains several inodes. If * the force flag is set, then the dependencies will be * cleared so that the update can always be made. Note that * the buffer is locked when this routine is called, so we * will never be in the middle of writing the inode block * to disk. */ void softdep_update_inodeblock(ip, bp, waitfor) struct inode *ip; /* the "in_core" copy of the inode */ struct buf *bp; /* the buffer containing the inode block */ int waitfor; /* nonzero => update must be allowed */ { struct inodedep *inodedep; struct worklist *wk; int error, gotit; /* * If the effective link count is not equal to the actual link * count, then we must track the difference in an inodedep while * the inode is (potentially) tossed out of the cache. Otherwise, * if there is no existing inodedep, then there are no dependencies * to track. */ ACQUIRE_LOCK(&lk); if (inodedep_lookup(ip->i_fs, ip->i_number, 0, &inodedep) == 0) { FREE_LOCK(&lk); if (ip->i_effnlink != ip->i_ffs_nlink) panic("softdep_update_inodeblock: bad link count"); return; } if (inodedep->id_nlinkdelta != ip->i_ffs_nlink - ip->i_effnlink) { FREE_LOCK(&lk); panic("softdep_update_inodeblock: bad delta"); } /* * Changes have been initiated. Anything depending on these * changes cannot occur until this inode has been written. */ inodedep->id_state &= ~COMPLETE; if ((inodedep->id_state & ONWORKLIST) == 0) WORKLIST_INSERT(&bp->b_dep, &inodedep->id_list); /* * Any new dependencies associated with the incore inode must * now be moved to the list associated with the buffer holding * the in-memory copy of the inode. Once merged process any * allocdirects that are completed by the merger. */ merge_inode_lists(inodedep); if (TAILQ_FIRST(&inodedep->id_inoupdt) != NULL) handle_allocdirect_partdone(TAILQ_FIRST(&inodedep->id_inoupdt)); /* * Now that the inode has been pushed into the buffer, the * operations dependent on the inode being written to disk * can be moved to the id_bufwait so that they will be * processed when the buffer I/O completes. */ while ((wk = LIST_FIRST(&inodedep->id_inowait)) != NULL) { WORKLIST_REMOVE(wk); WORKLIST_INSERT(&inodedep->id_bufwait, wk); } /* * Newly allocated inodes cannot be written until the bitmap * that allocates them have been written (indicated by * DEPCOMPLETE being set in id_state). If we are doing a * forced sync (e.g., an fsync on a file), we force the bitmap * to be written so that the update can be done. */ if ((inodedep->id_state & DEPCOMPLETE) != 0 || waitfor == 0) { FREE_LOCK(&lk); return; } bp = inodedep->id_buf; gotit = getdirtybuf(&bp, MNT_WAIT); FREE_LOCK(&lk); if (gotit && (error = bwrite(bp)) != 0) softdep_error("softdep_update_inodeblock: bwrite", error); if ((inodedep->id_state & DEPCOMPLETE) == 0) panic("softdep_update_inodeblock: update failed"); } /* * Merge the new inode dependency list (id_newinoupdt) into the old * inode dependency list (id_inoupdt). This routine must be called * with splbio interrupts blocked. */ STATIC void merge_inode_lists(inodedep) struct inodedep *inodedep; { struct allocdirect *listadp, *newadp; newadp = TAILQ_FIRST(&inodedep->id_newinoupdt); for (listadp = TAILQ_FIRST(&inodedep->id_inoupdt); listadp && newadp;) { if (listadp->ad_lbn < newadp->ad_lbn) { listadp = TAILQ_NEXT(listadp, ad_next); continue; } TAILQ_REMOVE(&inodedep->id_newinoupdt, newadp, ad_next); TAILQ_INSERT_BEFORE(listadp, newadp, ad_next); if (listadp->ad_lbn == newadp->ad_lbn) { allocdirect_merge(&inodedep->id_inoupdt, newadp, listadp); listadp = newadp; } newadp = TAILQ_FIRST(&inodedep->id_newinoupdt); } while ((newadp = TAILQ_FIRST(&inodedep->id_newinoupdt)) != NULL) { TAILQ_REMOVE(&inodedep->id_newinoupdt, newadp, ad_next); TAILQ_INSERT_TAIL(&inodedep->id_inoupdt, newadp, ad_next); } } /* * If we are doing an fsync, then we must ensure that any directory * entries for the inode have been written after the inode gets to disk. */ int softdep_fsync(vp) struct vnode *vp; /* the "in_core" copy of the inode */ { struct inodedep *inodedep; struct pagedep *pagedep; struct worklist *wk; struct diradd *dap; struct mount *mnt; struct vnode *pvp; struct inode *ip; struct inode *pip; struct buf *bp; struct fs *fs; struct proc *p = CURPROC; /* XXX */ int error, flushparent; ino_t parentino; ufs_lbn_t lbn; ip = VTOI(vp); fs = ip->i_fs; ACQUIRE_LOCK(&lk); if (inodedep_lookup(fs, ip->i_number, 0, &inodedep) == 0) { FREE_LOCK(&lk); return (0); } if (LIST_FIRST(&inodedep->id_inowait) != NULL || LIST_FIRST(&inodedep->id_bufwait) != NULL || TAILQ_FIRST(&inodedep->id_inoupdt) != NULL || TAILQ_FIRST(&inodedep->id_newinoupdt) != NULL) { FREE_LOCK(&lk); panic("softdep_fsync: pending ops"); } for (error = 0, flushparent = 0; ; ) { if ((wk = LIST_FIRST(&inodedep->id_pendinghd)) == NULL) break; if (wk->wk_type != D_DIRADD) { FREE_LOCK(&lk); panic("softdep_fsync: Unexpected type %s", TYPENAME(wk->wk_type)); } dap = WK_DIRADD(wk); /* * Flush our parent if this directory entry has a MKDIR_PARENT * dependency or is contained in a newly allocated block. */ if (dap->da_state & DIRCHG) pagedep = dap->da_previous->dm_pagedep; else pagedep = dap->da_pagedep; mnt = pagedep->pd_mnt; parentino = pagedep->pd_ino; lbn = pagedep->pd_lbn; if ((dap->da_state & (MKDIR_BODY | COMPLETE)) != COMPLETE) { FREE_LOCK(&lk); panic("softdep_fsync: dirty"); } if ((dap->da_state & MKDIR_PARENT) || (pagedep->pd_state & NEWBLOCK)) flushparent = 1; else flushparent = 0; /* * If we are being fsync'ed as part of vgone'ing this vnode, * then we will not be able to release and recover the * vnode below, so we just have to give up on writing its * directory entry out. It will eventually be written, just * not now, but then the user was not asking to have it * written, so we are not breaking any promises. */ if (vp->v_flag & VXLOCK) break; /* * We prevent deadlock by always fetching inodes from the * root, moving down the directory tree. Thus, when fetching * our parent directory, we must unlock ourselves before * requesting the lock on our parent. See the comment in * ufs_lookup for details on possible races. */ FREE_LOCK(&lk); VOP_UNLOCK(vp, 0, p); error = VFS_VGET(mnt, parentino, &pvp); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (error != 0) return (error); /* * All MKDIR_PARENT dependencies and all the NEWBLOCK pagedeps * that are contained in direct blocks will be resolved by * doing a UFS_UPDATE. Pagedeps contained in indirect blocks * may require a complete sync'ing of the directory. So, we * try the cheap and fast UFS_UPDATE first, and if that fails, * then we do the slower VOP_FSYNC of the directory. */ pip = VTOI(pvp); if (flushparent) { error = UFS_UPDATE(pip, MNT_WAIT); if (error) { vput(pvp); return (error); } if (pagedep->pd_state & NEWBLOCK) { error = VOP_FSYNC(pvp, p->p_ucred, MNT_WAIT, p); if (error) { vput(pvp); return (error); } } } /* * Flush directory page containing the inode's name. */ error = bread(pvp, lbn, blksize(fs, pip, lbn), p->p_ucred, &bp); if (error == 0) error = bwrite(bp); else brelse(bp); vput(pvp); if (error != 0) return (error); ACQUIRE_LOCK(&lk); if (inodedep_lookup(fs, ip->i_number, 0, &inodedep) == 0) break; } FREE_LOCK(&lk); return (0); } /* * Flush all the dirty bitmaps associated with the block device * before flushing the rest of the dirty blocks so as to reduce * the number of dependencies that will have to be rolled back. */ void softdep_fsync_mountdev(vp) struct vnode *vp; { struct buf *bp, *nbp; struct worklist *wk; if (!vn_isdisk(vp, NULL)) panic("softdep_fsync_mountdev: vnode not a disk"); ACQUIRE_LOCK(&lk); for (bp = LIST_FIRST(&vp->v_dirtyblkhd); bp; bp = nbp) { nbp = LIST_NEXT(bp, b_vnbufs); /* * If it is already scheduled, skip to the next buffer. */ if (bp->b_flags & B_BUSY) continue; bp->b_flags |= B_BUSY; if ((bp->b_flags & B_DELWRI) == 0) { FREE_LOCK(&lk); panic("softdep_fsync_mountdev: not dirty"); } /* * We are only interested in bitmaps with outstanding * dependencies. */ if ((wk = LIST_FIRST(&bp->b_dep)) == NULL || wk->wk_type != D_BMSAFEMAP) { bp->b_flags &= ~B_BUSY; continue; } bremfree(bp); FREE_LOCK(&lk); (void) bawrite(bp); ACQUIRE_LOCK(&lk); /* * Since we may have slept during the I/O, we need * to start from a known point. */ nbp = LIST_FIRST(&vp->v_dirtyblkhd); } drain_output(vp, 1); FREE_LOCK(&lk); } /* * This routine is called when we are trying to synchronously flush a * file. This routine must eliminate any filesystem metadata dependencies * so that the syncing routine can succeed by pushing the dirty blocks * associated with the file. If any I/O errors occur, they are returned. */ int softdep_sync_metadata(ap) struct vop_fsync_args /* { struct vnode *a_vp; struct ucred *a_cred; int a_waitfor; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct pagedep *pagedep; struct allocdirect *adp; struct allocindir *aip; struct buf *bp, *nbp; struct worklist *wk; int i, error, waitfor; /* * Check whether this vnode is involved in a filesystem * that is doing soft dependency processing. */ if (!vn_isdisk(vp, NULL)) { if (!DOINGSOFTDEP(vp)) return (0); } else if (vp->v_specmountpoint == NULL || (vp->v_specmountpoint->mnt_flag & MNT_SOFTDEP) == 0) return (0); /* * Ensure that any direct block dependencies have been cleared. */ ACQUIRE_LOCK(&lk); if ((error = flush_inodedep_deps(VTOI(vp)->i_fs, VTOI(vp)->i_number))) { FREE_LOCK(&lk); return (error); } /* * For most files, the only metadata dependencies are the * cylinder group maps that allocate their inode or blocks. * The block allocation dependencies can be found by traversing * the dependency lists for any buffers that remain on their * dirty buffer list. The inode allocation dependency will * be resolved when the inode is updated with MNT_WAIT. * This work is done in two passes. The first pass grabs most * of the buffers and begins asynchronously writing them. The * only way to wait for these asynchronous writes is to sleep * on the filesystem vnode which may stay busy for a long time * if the filesystem is active. So, instead, we make a second * pass over the dependencies blocking on each write. In the * usual case we will be blocking against a write that we * initiated, so when it is done the dependency will have been * resolved. Thus the second pass is expected to end quickly. */ waitfor = MNT_NOWAIT; top: /* * We must wait for any I/O in progress to finish so that * all potential buffers on the dirty list will be visible. */ drain_output(vp, 1); bp = LIST_FIRST(&vp->v_dirtyblkhd); if (getdirtybuf(&bp, MNT_WAIT) == 0) { FREE_LOCK(&lk); return (0); } loop: /* * As we hold the buffer locked, none of its dependencies * will disappear. */ LIST_FOREACH(wk, &bp->b_dep, wk_list) { switch (wk->wk_type) { case D_ALLOCDIRECT: adp = WK_ALLOCDIRECT(wk); if (adp->ad_state & DEPCOMPLETE) break; nbp = adp->ad_buf; if (getdirtybuf(&nbp, waitfor) == 0) break; FREE_LOCK(&lk); if (waitfor == MNT_NOWAIT) { bawrite(nbp); } else if ((error = VOP_BWRITE(nbp)) != 0) { bawrite(bp); return (error); } ACQUIRE_LOCK(&lk); break; case D_ALLOCINDIR: aip = WK_ALLOCINDIR(wk); if (aip->ai_state & DEPCOMPLETE) break; nbp = aip->ai_buf; if (getdirtybuf(&nbp, waitfor) == 0) break; FREE_LOCK(&lk); if (waitfor == MNT_NOWAIT) { bawrite(nbp); } else if ((error = VOP_BWRITE(nbp)) != 0) { bawrite(bp); return (error); } ACQUIRE_LOCK(&lk); break; case D_INDIRDEP: restart: LIST_FOREACH(aip, &WK_INDIRDEP(wk)->ir_deplisthd, ai_next) { if (aip->ai_state & DEPCOMPLETE) continue; nbp = aip->ai_buf; if (getdirtybuf(&nbp, MNT_WAIT) == 0) goto restart; FREE_LOCK(&lk); if ((error = VOP_BWRITE(nbp)) != 0) { bawrite(bp); return (error); } ACQUIRE_LOCK(&lk); goto restart; } break; case D_INODEDEP: if ((error = flush_inodedep_deps(WK_INODEDEP(wk)->id_fs, WK_INODEDEP(wk)->id_ino)) != 0) { FREE_LOCK(&lk); bawrite(bp); return (error); } break; case D_PAGEDEP: /* * We are trying to sync a directory that may * have dependencies on both its own metadata * and/or dependencies on the inodes of any * recently allocated files. We walk its diradd * lists pushing out the associated inode. */ pagedep = WK_PAGEDEP(wk); for (i = 0; i < DAHASHSZ; i++) { if (LIST_FIRST(&pagedep->pd_diraddhd[i]) == 0) continue; if ((error = flush_pagedep_deps(vp, pagedep->pd_mnt, &pagedep->pd_diraddhd[i]))) { FREE_LOCK(&lk); bawrite(bp); return (error); } } break; case D_MKDIR: /* * This case should never happen if the vnode has * been properly sync'ed. However, if this function * is used at a place where the vnode has not yet * been sync'ed, this dependency can show up. So, * rather than panic, just flush it. */ nbp = WK_MKDIR(wk)->md_buf; if (getdirtybuf(&nbp, waitfor) == 0) break; FREE_LOCK(&lk); if (waitfor == MNT_NOWAIT) { bawrite(nbp); } else if ((error = VOP_BWRITE(nbp)) != 0) { bawrite(bp); return (error); } ACQUIRE_LOCK(&lk); break; case D_BMSAFEMAP: /* * This case should never happen if the vnode has * been properly sync'ed. However, if this function * is used at a place where the vnode has not yet * been sync'ed, this dependency can show up. So, * rather than panic, just flush it. */ nbp = WK_BMSAFEMAP(wk)->sm_buf; if (getdirtybuf(&nbp, waitfor) == 0) break; FREE_LOCK(&lk); if (waitfor == MNT_NOWAIT) { bawrite(nbp); } else if ((error = VOP_BWRITE(nbp)) != 0) { bawrite(bp); return (error); } ACQUIRE_LOCK(&lk); break; default: FREE_LOCK(&lk); panic("softdep_sync_metadata: Unknown type %s", TYPENAME(wk->wk_type)); /* NOTREACHED */ } } nbp = LIST_NEXT(bp, b_vnbufs); getdirtybuf(&nbp, MNT_WAIT); FREE_LOCK(&lk); bawrite(bp); ACQUIRE_LOCK(&lk); if (nbp != NULL) { bp = nbp; goto loop; } /* * The brief unlock is to allow any pent up dependency * processing to be done. Then proceed with the second pass. */ if (waitfor == MNT_NOWAIT) { waitfor = MNT_WAIT; FREE_LOCK(&lk); ACQUIRE_LOCK(&lk); goto top; } /* * If we have managed to get rid of all the dirty buffers, * then we are done. For certain directories and block * devices, we may need to do further work. * * We must wait for any I/O in progress to finish so that * all potential buffers on the dirty list will be visible. */ drain_output(vp, 1); if (LIST_FIRST(&vp->v_dirtyblkhd) == NULL) { FREE_LOCK(&lk); return (0); } FREE_LOCK(&lk); /* * If we are trying to sync a block device, some of its buffers may * contain metadata that cannot be written until the contents of some * partially written files have been written to disk. The only easy * way to accomplish this is to sync the entire filesystem (luckily * this happens rarely). */ if (vn_isdisk(vp, NULL) && vp->v_specmountpoint && !VOP_ISLOCKED(vp) && (error = VFS_SYNC(vp->v_specmountpoint, MNT_WAIT, ap->a_cred, ap->a_p)) != 0) return (error); return (0); } /* * Flush the dependencies associated with an inodedep. * Called with splbio blocked. */ STATIC int flush_inodedep_deps(fs, ino) struct fs *fs; ino_t ino; { struct inodedep *inodedep; struct allocdirect *adp; int error, waitfor; struct buf *bp; /* * This work is done in two passes. The first pass grabs most * of the buffers and begins asynchronously writing them. The * only way to wait for these asynchronous writes is to sleep * on the filesystem vnode which may stay busy for a long time * if the filesystem is active. So, instead, we make a second * pass over the dependencies blocking on each write. In the * usual case we will be blocking against a write that we * initiated, so when it is done the dependency will have been * resolved. Thus the second pass is expected to end quickly. * We give a brief window at the top of the loop to allow * any pending I/O to complete. */ for (waitfor = MNT_NOWAIT; ; ) { FREE_LOCK(&lk); ACQUIRE_LOCK(&lk); if (inodedep_lookup(fs, ino, 0, &inodedep) == 0) return (0); TAILQ_FOREACH(adp, &inodedep->id_inoupdt, ad_next) { if (adp->ad_state & DEPCOMPLETE) continue; bp = adp->ad_buf; if (getdirtybuf(&bp, waitfor) == 0) { if (waitfor == MNT_NOWAIT) continue; break; } FREE_LOCK(&lk); if (waitfor == MNT_NOWAIT) { bawrite(bp); } else if ((error = VOP_BWRITE(bp)) != 0) { ACQUIRE_LOCK(&lk); return (error); } ACQUIRE_LOCK(&lk); break; } if (adp != NULL) continue; TAILQ_FOREACH(adp, &inodedep->id_newinoupdt, ad_next) { if (adp->ad_state & DEPCOMPLETE) continue; bp = adp->ad_buf; if (getdirtybuf(&bp, waitfor) == 0) { if (waitfor == MNT_NOWAIT) continue; break; } FREE_LOCK(&lk); if (waitfor == MNT_NOWAIT) { bawrite(bp); } else if ((error = VOP_BWRITE(bp)) != 0) { ACQUIRE_LOCK(&lk); return (error); } ACQUIRE_LOCK(&lk); break; } if (adp != NULL) continue; /* * If pass2, we are done, otherwise do pass 2. */ if (waitfor == MNT_WAIT) break; waitfor = MNT_WAIT; } /* * Try freeing inodedep in case all dependencies have been removed. */ if (inodedep_lookup(fs, ino, 0, &inodedep) != 0) (void) free_inodedep(inodedep); return (0); } /* * Eliminate a pagedep dependency by flushing out all its diradd dependencies. * Called with splbio blocked. */ STATIC int flush_pagedep_deps(pvp, mp, diraddhdp) struct vnode *pvp; struct mount *mp; struct diraddhd *diraddhdp; { struct proc *p = CURPROC; /* XXX */ struct worklist *wk; struct inodedep *inodedep; struct ufsmount *ump; struct diradd *dap; struct vnode *vp; int gotit, error = 0; struct buf *bp; ino_t inum; ump = VFSTOUFS(mp); while ((dap = LIST_FIRST(diraddhdp)) != NULL) { /* * Flush ourselves if this directory entry * has a MKDIR_PARENT dependency. */ if (dap->da_state & MKDIR_PARENT) { FREE_LOCK(&lk); if ((error = UFS_UPDATE(VTOI(pvp), MNT_WAIT))) break; ACQUIRE_LOCK(&lk); /* * If that cleared dependencies, go on to next. */ if (dap != LIST_FIRST(diraddhdp)) continue; if (dap->da_state & MKDIR_PARENT) { FREE_LOCK(&lk); panic("flush_pagedep_deps: MKDIR_PARENT"); } } /* * A newly allocated directory must have its "." and * ".." entries written out before its name can be * committed in its parent. We do not want or need * the full semantics of a synchronous VOP_FSYNC as * that may end up here again, once for each directory * level in the filesystem. Instead, we push the blocks * and wait for them to clear. We have to fsync twice * because the first call may choose to defer blocks * that still have dependencies, but deferral will * happen at most once. */ inum = dap->da_newinum; if (dap->da_state & MKDIR_BODY) { FREE_LOCK(&lk); if ((error = VFS_VGET(mp, inum, &vp)) != 0) break; if ((error=VOP_FSYNC(vp, p->p_ucred, MNT_NOWAIT, p)) || (error=VOP_FSYNC(vp, p->p_ucred, MNT_NOWAIT, p))) { vput(vp); break; } drain_output(vp, 0); /* * If first block is still dirty with a D_MKDIR * dependency then it needs to be written now. */ for (;;) { error = 0; ACQUIRE_LOCK(&lk); bp = incore(vp, 0); if (bp == NULL) { FREE_LOCK(&lk); break; } LIST_FOREACH(wk, &bp->b_dep, wk_list) if (wk->wk_type == D_MKDIR) break; if (wk) { gotit = getdirtybuf(&bp, MNT_WAIT); FREE_LOCK(&lk); if (gotit && (error = bwrite(bp)) != 0) break; } else FREE_LOCK(&lk); break; } vput(vp); /* Flushing of first block failed */ if (error) break; ACQUIRE_LOCK(&lk); /* * If that cleared dependencies, go on to next. */ if (dap != LIST_FIRST(diraddhdp)) continue; if (dap->da_state & MKDIR_BODY) { FREE_LOCK(&lk); panic("flush_pagedep_deps: MKDIR_BODY"); } } /* * Flush the inode on which the directory entry depends. * Having accounted for MKDIR_PARENT and MKDIR_BODY above, * the only remaining dependency is that the updated inode * count must get pushed to disk. The inode has already * been pushed into its inode buffer (via VOP_UPDATE) at * the time of the reference count change. So we need only * locate that buffer, ensure that there will be no rollback * caused by a bitmap dependency, then write the inode buffer. */ if (inodedep_lookup(ump->um_fs, inum, 0, &inodedep) == 0) { FREE_LOCK(&lk); panic("flush_pagedep_deps: lost inode"); } /* * If the inode still has bitmap dependencies, * push them to disk. */ if ((inodedep->id_state & DEPCOMPLETE) == 0) { bp = inodedep->id_buf; gotit = getdirtybuf(&bp, MNT_WAIT); FREE_LOCK(&lk); if (gotit && (error = bwrite(bp)) != 0) break; ACQUIRE_LOCK(&lk); if (dap != LIST_FIRST(diraddhdp)) continue; } /* * If the inode is still sitting in a buffer waiting * to be written, push it to disk. */ FREE_LOCK(&lk); if ((error = bread(ump->um_devvp, fsbtodb(ump->um_fs, ino_to_fsba(ump->um_fs, inum)), (int)ump->um_fs->fs_bsize, NOCRED, &bp)) != 0) { brelse(bp); break; } if ((error = bwrite(bp)) != 0) break; ACQUIRE_LOCK(&lk); /* * If we have failed to get rid of all the dependencies * then something is seriously wrong. */ if (dap == LIST_FIRST(diraddhdp)) { FREE_LOCK(&lk); panic("flush_pagedep_deps: flush failed"); } } if (error) ACQUIRE_LOCK(&lk); return (error); } /* * A large burst of file addition or deletion activity can drive the * memory load excessively high. First attempt to slow things down * using the techniques below. If that fails, this routine requests * the offending operations to fall back to running synchronously * until the memory load returns to a reasonable level. */ int softdep_slowdown(vp) struct vnode *vp; { int max_softdeps_hard; max_softdeps_hard = max_softdeps * 11 / 10; if (num_dirrem < max_softdeps_hard / 2 && num_inodedep < max_softdeps_hard) return (0); stat_sync_limit_hit += 1; return (1); } /* * If memory utilization has gotten too high, deliberately slow things * down and speed up the I/O processing. */ STATIC int request_cleanup(resource, islocked) int resource; int islocked; { struct proc *p = CURPROC; int s; /* * We never hold up the filesystem syncer process. */ if (p == filesys_syncer || (p->p_flag & P_SOFTDEP)) return (0); /* * First check to see if the work list has gotten backlogged. * If it has, co-opt this process to help clean up two entries. * Because this process may hold inodes locked, we cannot * handle any remove requests that might block on a locked * inode as that could lead to deadlock. We set P_SOFTDEP * to avoid recursively processing the worklist. */ if (num_on_worklist > max_softdeps / 10) { p->p_flag |= P_SOFTDEP; if (islocked) FREE_LOCK(&lk); process_worklist_item(NULL, LK_NOWAIT); process_worklist_item(NULL, LK_NOWAIT); p->p_flag &= ~P_SOFTDEP; stat_worklist_push += 2; if (islocked) ACQUIRE_LOCK(&lk); return(1); } /* * Next, we attempt to speed up the syncer process. If that * is successful, then we allow the process to continue. */ if (speedup_syncer()) return(0); /* * If we are resource constrained on inode dependencies, try * flushing some dirty inodes. Otherwise, we are constrained * by file deletions, so try accelerating flushes of directories * with removal dependencies. We would like to do the cleanup * here, but we probably hold an inode locked at this point and * that might deadlock against one that we try to clean. So, * the best that we can do is request the syncer daemon to do * the cleanup for us. */ switch (resource) { case FLUSH_INODES: stat_ino_limit_push += 1; req_clear_inodedeps += 1; stat_countp = &stat_ino_limit_hit; break; case FLUSH_REMOVE: stat_blk_limit_push += 1; req_clear_remove += 1; stat_countp = &stat_blk_limit_hit; break; default: if (islocked) FREE_LOCK(&lk); panic("request_cleanup: unknown type"); } /* * Hopefully the syncer daemon will catch up and awaken us. * We wait at most tickdelay before proceeding in any case. */ if (islocked == 0) ACQUIRE_LOCK(&lk); proc_waiting += 1; if (!timeout_pending(&proc_waiting_timeout)) timeout_add(&proc_waiting_timeout, tickdelay > 2 ? tickdelay : 2); s = FREE_LOCK_INTERLOCKED(&lk); (void) tsleep((caddr_t)&proc_waiting, PPAUSE, "softupdate", 0); ACQUIRE_LOCK_INTERLOCKED(&lk, s); proc_waiting -= 1; if (islocked == 0) FREE_LOCK(&lk); return (1); } /* * Awaken processes pausing in request_cleanup and clear proc_waiting * to indicate that there is no longer a timer running. */ void pause_timer(arg) void *arg; { *stat_countp += 1; wakeup_one(&proc_waiting); if (proc_waiting > 0) timeout_add(&proc_waiting_timeout, tickdelay > 2 ? tickdelay : 2); } /* * Flush out a directory with at least one removal dependency in an effort to * reduce the number of dirrem, freefile, and freeblks dependency structures. */ STATIC void clear_remove(p) struct proc *p; { struct pagedep_hashhead *pagedephd; struct pagedep *pagedep; static int next = 0; struct mount *mp; struct vnode *vp; int error, cnt; ino_t ino; ACQUIRE_LOCK(&lk); for (cnt = 0; cnt < pagedep_hash; cnt++) { pagedephd = &pagedep_hashtbl[next++]; if (next >= pagedep_hash) next = 0; LIST_FOREACH(pagedep, pagedephd, pd_hash) { if (LIST_FIRST(&pagedep->pd_dirremhd) == NULL) continue; mp = pagedep->pd_mnt; ino = pagedep->pd_ino; #if 0 if (vn_start_write(NULL, &mp, V_NOWAIT) != 0) continue; #endif FREE_LOCK(&lk); if ((error = VFS_VGET(mp, ino, &vp)) != 0) { softdep_error("clear_remove: vget", error); #if 0 vn_finished_write(mp); #endif return; } if ((error = VOP_FSYNC(vp, p->p_ucred, MNT_NOWAIT, p))) softdep_error("clear_remove: fsync", error); drain_output(vp, 0); vput(vp); #if 0 vn_finished_write(mp); #endif return; } } FREE_LOCK(&lk); } /* * Clear out a block of dirty inodes in an effort to reduce * the number of inodedep dependency structures. */ STATIC void clear_inodedeps(p) struct proc *p; { struct inodedep_hashhead *inodedephd; struct inodedep *inodedep = NULL; static int next = 0; struct mount *mp; struct vnode *vp; struct fs *fs; int error, cnt; ino_t firstino, lastino, ino; ACQUIRE_LOCK(&lk); /* * Pick a random inode dependency to be cleared. * We will then gather up all the inodes in its block * that have dependencies and flush them out. */ for (cnt = 0; cnt < inodedep_hash; cnt++) { inodedephd = &inodedep_hashtbl[next++]; if (next >= inodedep_hash) next = 0; if ((inodedep = LIST_FIRST(inodedephd)) != NULL) break; } if (inodedep == NULL) { FREE_LOCK(&lk); return; } /* * Ugly code to find mount point given pointer to superblock. */ fs = inodedep->id_fs; CIRCLEQ_FOREACH(mp, &mountlist, mnt_list) if ((mp->mnt_flag & MNT_SOFTDEP) && fs == VFSTOUFS(mp)->um_fs) break; /* * Find the last inode in the block with dependencies. */ firstino = inodedep->id_ino & ~(INOPB(fs) - 1); for (lastino = firstino + INOPB(fs) - 1; lastino > firstino; lastino--) if (inodedep_lookup(fs, lastino, 0, &inodedep) != 0) break; /* * Asynchronously push all but the last inode with dependencies. * Synchronously push the last inode with dependencies to ensure * that the inode block gets written to free up the inodedeps. */ for (ino = firstino; ino <= lastino; ino++) { if (inodedep_lookup(fs, ino, 0, &inodedep) == 0) continue; FREE_LOCK(&lk); #if 0 if (vn_start_write(NULL, &mp, V_NOWAIT) != 0) continue; #endif if ((error = VFS_VGET(mp, ino, &vp)) != 0) { softdep_error("clear_inodedeps: vget", error); #if 0 vn_finished_write(mp); #endif return; } if (ino == lastino) { if ((error = VOP_FSYNC(vp, p->p_ucred, MNT_WAIT, p))) softdep_error("clear_inodedeps: fsync1", error); } else { if ((error = VOP_FSYNC(vp, p->p_ucred, MNT_NOWAIT, p))) softdep_error("clear_inodedeps: fsync2", error); drain_output(vp, 0); } vput(vp); #if 0 vn_finished_write(mp); #endif ACQUIRE_LOCK(&lk); } FREE_LOCK(&lk); } /* * Function to determine if the buffer has outstanding dependencies * that will cause a roll-back if the buffer is written. If wantcount * is set, return number of dependencies, otherwise just yes or no. */ int softdep_count_dependencies(bp, wantcount, islocked) struct buf *bp; int wantcount; int islocked; { struct worklist *wk; struct inodedep *inodedep; struct indirdep *indirdep; struct allocindir *aip; struct pagedep *pagedep; struct diradd *dap; int i, retval; retval = 0; if (!islocked) ACQUIRE_LOCK(&lk); LIST_FOREACH(wk, &bp->b_dep, wk_list) { switch (wk->wk_type) { case D_INODEDEP: inodedep = WK_INODEDEP(wk); if ((inodedep->id_state & DEPCOMPLETE) == 0) { /* bitmap allocation dependency */ retval += 1; if (!wantcount) goto out; } if (TAILQ_FIRST(&inodedep->id_inoupdt)) { /* direct block pointer dependency */ retval += 1; if (!wantcount) goto out; } continue; case D_INDIRDEP: indirdep = WK_INDIRDEP(wk); LIST_FOREACH(aip, &indirdep->ir_deplisthd, ai_next) { /* indirect block pointer dependency */ retval += 1; if (!wantcount) goto out; } continue; case D_PAGEDEP: pagedep = WK_PAGEDEP(wk); for (i = 0; i < DAHASHSZ; i++) { LIST_FOREACH(dap, &pagedep->pd_diraddhd[i], da_pdlist) { /* directory entry dependency */ retval += 1; if (!wantcount) goto out; } } continue; case D_BMSAFEMAP: case D_ALLOCDIRECT: case D_ALLOCINDIR: case D_MKDIR: /* never a dependency on these blocks */ continue; default: if (!islocked) FREE_LOCK(&lk); panic("softdep_check_for_rollback: Unexpected type %s", TYPENAME(wk->wk_type)); /* NOTREACHED */ } } out: if (!islocked) FREE_LOCK(&lk); return retval; } /* * Acquire exclusive access to a buffer. * Must be called with splbio blocked. * Return 1 if buffer was acquired. */ STATIC int getdirtybuf(bpp, waitfor) struct buf **bpp; int waitfor; { struct buf *bp; int s; for (;;) { if ((bp = *bpp) == NULL) return (0); if ((bp->b_flags & B_BUSY) == 0) break; if (waitfor != MNT_WAIT) return (0); bp->b_flags |= B_WANTED; s = FREE_LOCK_INTERLOCKED(&lk); tsleep((caddr_t)bp, PRIBIO + 1, "sdsdty", 0); ACQUIRE_LOCK_INTERLOCKED(&lk, s); } if ((bp->b_flags & B_DELWRI) == 0) return (0); bremfree(bp); bp->b_flags |= B_BUSY; return (1); } /* * Wait for pending output on a vnode to complete. * Must be called with vnode locked. */ STATIC void drain_output(vp, islocked) struct vnode *vp; int islocked; { int s; if (!islocked) ACQUIRE_LOCK(&lk); while (vp->v_numoutput) { vp->v_bioflag |= VBIOWAIT; s = FREE_LOCK_INTERLOCKED(&lk); tsleep((caddr_t)&vp->v_numoutput, PRIBIO + 1, "drain_output", 0); ACQUIRE_LOCK_INTERLOCKED(&lk, s); } if (!islocked) FREE_LOCK(&lk); } /* * Called whenever a buffer that is being invalidated or reallocated * contains dependencies. This should only happen if an I/O error has * occurred. The routine is called with the buffer locked. */ void softdep_deallocate_dependencies(bp) struct buf *bp; { if ((bp->b_flags & B_ERROR) == 0) panic("softdep_deallocate_dependencies: dangling deps"); softdep_error(bp->b_vp->v_mount->mnt_stat.f_mntonname, bp->b_error); panic("softdep_deallocate_dependencies: unrecovered I/O error"); } /* * Function to handle asynchronous write errors in the filesystem. */ void softdep_error(func, error) char *func; int error; { /* XXX should do something better! */ printf("%s: got error %d while accessing filesystem\n", func, error); } makefs/src/sys/ufs/ffs/ffs_softdep_stub.c010064400000000000000000000126041027001310300157240ustar00/* $OpenBSD: ffs_softdep_stub.c,v 1.11 2005/07/20 16:30:35 pedro Exp $ */ /* * Copyright 1998 Marshall Kirk McKusick. All Rights Reserved. * * The soft updates code is derived from the appendix of a University * of Michigan technical report (Gregory R. Ganger and Yale N. Patt, * "Soft Updates: A Solution to the Metadata Update Problem in File * Systems", CSE-TR-254-95, August 1995). * * 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. None of the names of McKusick, Ganger, or the University of Michigan * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY MARSHALL KIRK MCKUSICK ``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 MARSHALL KIRK MCKUSICK 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: @(#)ffs_softdep_stub.c 9.1 (McKusick) 7/10/97 * $FreeBSD: src/sys/ufs/ffs/ffs_softdep_stub.c,v 1.14 2000/08/09 00:41:54 tegge Exp $ */ #ifndef FFS_SOFTUPDATES #include #include #include #include #include #include #include int softdep_flushfiles(oldmnt, flags, p) struct mount *oldmnt; int flags; struct proc *p; { panic("softdep_flushfiles called"); } int softdep_mount(devvp, mp, fs, cred) struct vnode *devvp; struct mount *mp; struct fs *fs; struct ucred *cred; { return (0); } void softdep_initialize() { return; } void softdep_setup_inomapdep(bp, ip, newinum) struct buf *bp; struct inode *ip; ino_t newinum; { panic("softdep_setup_inomapdep called"); } void softdep_setup_blkmapdep(bp, fs, newblkno) struct buf *bp; struct fs *fs; daddr_t newblkno; { panic("softdep_setup_blkmapdep called"); } void softdep_setup_allocdirect(ip, lbn, newblkno, oldblkno, newsize, oldsize, bp) struct inode *ip; ufs_lbn_t lbn; daddr_t newblkno; daddr_t oldblkno; long newsize; long oldsize; struct buf *bp; { panic("softdep_setup_allocdirect called"); } void softdep_setup_allocindir_page(ip, lbn, bp, ptrno, newblkno, oldblkno, nbp) struct inode *ip; ufs_lbn_t lbn; struct buf *bp; int ptrno; daddr_t newblkno; daddr_t oldblkno; struct buf *nbp; { panic("softdep_setup_allocindir_page called"); } void softdep_setup_allocindir_meta(nbp, ip, bp, ptrno, newblkno) struct buf *nbp; struct inode *ip; struct buf *bp; int ptrno; daddr_t newblkno; { panic("softdep_setup_allocindir_meta called"); } void softdep_setup_freeblocks(ip, length) struct inode *ip; off_t length; { panic("softdep_setup_freeblocks called"); } void softdep_freefile(pvp, ino, mode) struct vnode *pvp; ino_t ino; mode_t mode; { panic("softdep_freefile called"); } int softdep_setup_directory_add(bp, dp, diroffset, newinum, newdirbp, isnewblk) struct buf *bp; struct inode *dp; off_t diroffset; long newinum; struct buf *newdirbp; int isnewblk; { panic("softdep_setup_directory_add called"); return (0); } void softdep_change_directoryentry_offset(dp, base, oldloc, newloc, entrysize) struct inode *dp; caddr_t base; caddr_t oldloc; caddr_t newloc; int entrysize; { panic("softdep_change_directoryentry_offset called"); } void softdep_setup_remove(bp, dp, ip, isrmdir) struct buf *bp; struct inode *dp; struct inode *ip; int isrmdir; { panic("softdep_setup_remove called"); } void softdep_setup_directory_change(bp, dp, ip, newinum, isrmdir) struct buf *bp; struct inode *dp; struct inode *ip; long newinum; int isrmdir; { panic("softdep_setup_directory_change called"); } void softdep_change_linkcnt(ip, nodelay) struct inode *ip; int nodelay; { panic("softdep_change_linkcnt called"); } void softdep_load_inodeblock(ip) struct inode *ip; { panic("softdep_load_inodeblock called"); } void softdep_update_inodeblock(ip, bp, waitfor) struct inode *ip; struct buf *bp; int waitfor; { panic("softdep_update_inodeblock called"); } void softdep_fsync_mountdev(vp) struct vnode *vp; { return; } int softdep_flushworklist(oldmnt, countp, p) struct mount *oldmnt; int *countp; struct proc *p; { *countp = 0; return (0); } int softdep_sync_metadata(ap) struct vop_fsync_args /* { struct vnode *a_vp; struct ucred *a_cred; int a_waitfor; struct proc *a_p; } */ *ap; { return (0); } int softdep_slowdown(vp) struct vnode *vp; { panic("softdep_slowdown called"); } #endif /* !FFS_SOFTUPDATES */ makefs/src/sys/ufs/ffs/ffs_subr.c010064400000000000000000000144471020120120500142010ustar00/* $OpenBSD: ffs_subr.c,v 1.15 2004/01/20 03:44:06 tedu Exp $ */ /* $NetBSD: ffs_subr.c,v 1.6 1996/03/17 02:16:23 christos Exp $ */ /* * Copyright (c) 1982, 1986, 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. * * @(#)ffs_subr.c 8.2 (Berkeley) 9/21/93 */ #include #include #ifdef _KERNEL #include #include #include #include #include #include #include #include #include #include /* * Return buffer with the contents of block "offset" from the beginning of * directory "ip". If "res" is non-zero, fill it in with a pointer to the * remaining space in the directory. */ int ffs_bufatoff(struct inode *ip, off_t offset, char **res, struct buf **bpp) { struct fs *fs; struct vnode *vp; struct buf *bp; daddr_t lbn; int bsize, error; vp = ITOV(ip); fs = ip->i_fs; lbn = lblkno(fs, offset); bsize = blksize(fs, ip, lbn); *bpp = NULL; if ((error = bread(vp, lbn, bsize, NOCRED, &bp)) != 0) { brelse(bp); return (error); } if (res) *res = (char *)bp->b_data + blkoff(fs, offset); *bpp = bp; return (0); } #else /* Prototypes for userland */ void ffs_fragacct(struct fs *, int, int32_t[], int); int ffs_isfreeblock(struct fs *, unsigned char *, daddr_t); int ffs_isblock(struct fs *, unsigned char *, daddr_t); void ffs_clrblock(struct fs *, u_char *, daddr_t); void ffs_setblock(struct fs *, unsigned char *, daddr_t); __dead void panic(const char *, ...); #endif /* * Update the frsum fields to reflect addition or deletion * of some frags. */ void ffs_fragacct(fs, fragmap, fraglist, cnt) struct fs *fs; int fragmap; int32_t fraglist[]; int cnt; { int inblk; register int field, subfield; register int siz, pos; inblk = (int)(fragtbl[fs->fs_frag][fragmap]) << 1; fragmap <<= 1; for (siz = 1; siz < fs->fs_frag; siz++) { if ((inblk & (1 << (siz + (fs->fs_frag % NBBY)))) == 0) continue; field = around[siz]; subfield = inside[siz]; for (pos = siz; pos <= fs->fs_frag; pos++) { if ((fragmap & field) == subfield) { fraglist[siz] += cnt; pos += siz; field <<= siz; subfield <<= siz; } field <<= 1; subfield <<= 1; } } } #if defined(_KERNEL) && defined(DIAGNOSTIC) void ffs_checkoverlap(bp, ip) struct buf *bp; struct inode *ip; { register struct buf *ebp, *ep; register daddr_t start, last; struct vnode *vp; ebp = &buf[nbuf]; start = bp->b_blkno; last = start + btodb(bp->b_bcount) - 1; for (ep = buf; ep < ebp; ep++) { if (ep == bp || (ep->b_flags & B_INVAL) || ep->b_vp == NULLVP) continue; if (VOP_BMAP(ep->b_vp, (daddr_t)0, &vp, (daddr_t)0, NULL)) continue; if (vp != ip->i_devvp) continue; /* look for overlap */ if (ep->b_bcount == 0 || ep->b_blkno > last || ep->b_blkno + btodb(ep->b_bcount) <= start) continue; vprint("Disk overlap", vp); (void)printf("\tstart %d, end %d overlap start %d, end %ld\n", start, last, ep->b_blkno, ep->b_blkno + btodb(ep->b_bcount) - 1); panic("Disk buffer overlap"); } } #endif /* DIAGNOSTIC */ /* * block operations * * check if a block is available */ int ffs_isblock(fs, cp, h) struct fs *fs; unsigned char *cp; daddr_t h; { unsigned char mask; switch ((int)fs->fs_frag) { case 8: return (cp[h] == 0xff); case 4: mask = 0x0f << ((h & 0x1) << 2); return ((cp[h >> 1] & mask) == mask); case 2: mask = 0x03 << ((h & 0x3) << 1); return ((cp[h >> 2] & mask) == mask); case 1: mask = 0x01 << (h & 0x7); return ((cp[h >> 3] & mask) == mask); default: panic("ffs_isblock"); } } /* * take a block out of the map */ void ffs_clrblock(fs, cp, h) struct fs *fs; u_char *cp; daddr_t h; { switch ((int)fs->fs_frag) { case 8: cp[h] = 0; return; case 4: cp[h >> 1] &= ~(0x0f << ((h & 0x1) << 2)); return; case 2: cp[h >> 2] &= ~(0x03 << ((h & 0x3) << 1)); return; case 1: cp[h >> 3] &= ~(0x01 << (h & 0x7)); return; default: panic("ffs_clrblock"); } } /* * put a block into the map */ void ffs_setblock(fs, cp, h) struct fs *fs; unsigned char *cp; daddr_t h; { switch ((int)fs->fs_frag) { case 8: cp[h] = 0xff; return; case 4: cp[h >> 1] |= (0x0f << ((h & 0x1) << 2)); return; case 2: cp[h >> 2] |= (0x03 << ((h & 0x3) << 1)); return; case 1: cp[h >> 3] |= (0x01 << (h & 0x7)); return; default: panic("ffs_setblock"); } } /* * check if a block is free */ int ffs_isfreeblock(fs, cp, h) struct fs *fs; unsigned char *cp; daddr_t h; { switch ((int)fs->fs_frag) { case 8: return (cp[h] == 0); case 4: return ((cp[h >> 1] & (0x0f << ((h & 0x1) << 2))) == 0); case 2: return ((cp[h >> 2] & (0x03 << ((h & 0x3) << 1))) == 0); case 1: return ((cp[h >> 3] & (0x01 << (h & 0x7))) == 0); default: panic("ffs_isfreeblock"); } } makefs/src/sys/ufs/ffs/ffs_tables.c010064400000000000000000000133161341415245500145170ustar00/* $OpenBSD: ffs_tables.c,v 1.5 2003/08/26 16:10:57 mickey Exp $ */ /* $NetBSD: ffs_tables.c,v 1.2 1994/06/29 06:46:35 cgd Exp $ */ /* * Copyright (c) 1982, 1986, 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. * * @(#)ffs_tables.c 8.1 (Berkeley) 6/11/93 */ #include #ifdef GNUPORT #include #endif #include /* * Bit patterns for identifying fragments in the block map * used as ((map & around) == inside) */ const int around[9] = { 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff }; const int inside[9] = { 0x0, 0x2, 0x6, 0xe, 0x1e, 0x3e, 0x7e, 0xfe, 0x1fe }; /* * Given a block map bit pattern, the frag tables tell whether a * particular size fragment is available. * * used as: * if ((1 << (size - 1)) & fragtbl[fs->fs_frag][map] { * at least one fragment of the indicated size is available * } * * These tables are used by the scanc instruction on the VAX to * quickly find an appropriate fragment. */ const u_char fragtbl124[256] = { 0x00, 0x16, 0x16, 0x2a, 0x16, 0x16, 0x26, 0x4e, 0x16, 0x16, 0x16, 0x3e, 0x2a, 0x3e, 0x4e, 0x8a, 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e, 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e, 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e, 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e, 0x2a, 0x3e, 0x3e, 0x2a, 0x3e, 0x3e, 0x2e, 0x6e, 0x3e, 0x3e, 0x3e, 0x3e, 0x2a, 0x3e, 0x6e, 0xaa, 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e, 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e, 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e, 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e, 0x26, 0x36, 0x36, 0x2e, 0x36, 0x36, 0x26, 0x6e, 0x36, 0x36, 0x36, 0x3e, 0x2e, 0x3e, 0x6e, 0xae, 0x4e, 0x5e, 0x5e, 0x6e, 0x5e, 0x5e, 0x6e, 0x4e, 0x5e, 0x5e, 0x5e, 0x7e, 0x6e, 0x7e, 0x4e, 0xce, 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e, 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e, 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e, 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e, 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e, 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x7e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x7e, 0xbe, 0x2a, 0x3e, 0x3e, 0x2a, 0x3e, 0x3e, 0x2e, 0x6e, 0x3e, 0x3e, 0x3e, 0x3e, 0x2a, 0x3e, 0x6e, 0xaa, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x7e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x7e, 0xbe, 0x4e, 0x5e, 0x5e, 0x6e, 0x5e, 0x5e, 0x6e, 0x4e, 0x5e, 0x5e, 0x5e, 0x7e, 0x6e, 0x7e, 0x4e, 0xce, 0x8a, 0x9e, 0x9e, 0xaa, 0x9e, 0x9e, 0xae, 0xce, 0x9e, 0x9e, 0x9e, 0xbe, 0xaa, 0xbe, 0xce, 0x8a, }; const u_char fragtbl8[256] = { 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x04, 0x01, 0x01, 0x01, 0x03, 0x02, 0x03, 0x04, 0x08, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x02, 0x03, 0x03, 0x02, 0x04, 0x05, 0x08, 0x10, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09, 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06, 0x04, 0x05, 0x05, 0x06, 0x08, 0x09, 0x10, 0x20, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x03, 0x03, 0x03, 0x03, 0x05, 0x05, 0x09, 0x11, 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06, 0x03, 0x03, 0x03, 0x03, 0x02, 0x03, 0x06, 0x0a, 0x04, 0x05, 0x05, 0x06, 0x05, 0x05, 0x06, 0x04, 0x08, 0x09, 0x09, 0x0a, 0x10, 0x11, 0x20, 0x40, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x03, 0x03, 0x03, 0x03, 0x05, 0x05, 0x09, 0x11, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07, 0x05, 0x05, 0x05, 0x07, 0x09, 0x09, 0x11, 0x21, 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06, 0x03, 0x03, 0x03, 0x03, 0x02, 0x03, 0x06, 0x0a, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07, 0x02, 0x03, 0x03, 0x02, 0x06, 0x07, 0x0a, 0x12, 0x04, 0x05, 0x05, 0x06, 0x05, 0x05, 0x06, 0x04, 0x05, 0x05, 0x05, 0x07, 0x06, 0x07, 0x04, 0x0c, 0x08, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x0a, 0x0c, 0x10, 0x11, 0x11, 0x12, 0x20, 0x21, 0x40, 0x80, }; /* * The actual fragtbl array. */ const u_char *fragtbl[MAXFRAG + 1] = { 0, fragtbl124, fragtbl124, 0, fragtbl124, 0, 0, 0, fragtbl8, }; makefs/src/sys/ufs/ffs/ffs_vfsops.c010064400000000000000000001110261341414421700145570ustar00/** $MirOS: src/sys/ufs/ffs/ffs_vfsops.c,v 1.16 2019/01/05 15:08:36 tg Exp $ */ /* $OpenBSD: ffs_vfsops.c,v 1.70 2005/07/03 20:14:02 drahn Exp $ */ /* $NetBSD: ffs_vfsops.c,v 1.19 1996/02/09 22:22:26 christos Exp $ */ /* * Copyright (c) 2004 * Thorsten "mirabilos" Glaser * Copyright (c) 1989, 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. * * @(#)ffs_vfsops.c 8.14 (Berkeley) 11/28/94 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __GLIBC__ #include #endif #include #include #include #include #include #include #include #include #include #include int ffs_sbupdate(struct ufsmount *, int); int ffs_reload_vnode(struct vnode *, void *); int ffs_sync_vnode(struct vnode *, void *); const struct vfsops ffs_vfsops = { ffs_mount, ufs_start, ffs_unmount, ufs_root, ufs_quotactl, ffs_statfs, ffs_sync, ffs_vget, ffs_fhtovp, ffs_vptofh, ffs_init, ffs_sysctl, ufs_check_export }; struct inode_vtbl ffs_vtbl = { ffs_truncate, ffs_update, ffs_inode_alloc, ffs_inode_free, ffs_balloc, ffs_bufatoff }; extern u_long nextgennumber; /* * Called by main() when ufs is going to be mounted as root. */ struct pool ffs_ino_pool; int ffs_mountroot() { struct fs *fs; struct mount *mp; struct proc *p = curproc; /* XXX */ struct ufsmount *ump; int error; /* * Get vnodes for swapdev and rootdev. */ swapdev_vp = NULL; if ((error = bdevvp(swapdev, &swapdev_vp)) || (error = bdevvp(rootdev, &rootvp))) { printf("ffs_mountroot: can't setup bdevvps\n"); if (swapdev_vp) vrele(swapdev_vp); return (error); } if ((error = vfs_rootmountalloc("ffs", "root_device", &mp)) != 0) { vrele(swapdev_vp); vrele(rootvp); return (error); } if ((error = ffs_mountfs(rootvp, mp, p)) != 0) { mp->mnt_vfc->vfc_refcount--; vfs_unbusy(mp, p); free(mp, M_MOUNT); vrele(swapdev_vp); vrele(rootvp); return (error); } simple_lock(&mountlist_slock); CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list); simple_unlock(&mountlist_slock); ump = VFSTOUFS(mp); fs = ump->um_fs; (void) copystr(mp->mnt_stat.f_mntonname, fs->fs_fsmnt, MNAMELEN - 1, 0); (void)ffs_statfs(mp, &mp->mnt_stat, p); vfs_unbusy(mp, p); inittodr(fs->fs_time); return (0); } /* * VFS Operations. * * mount system call */ int ffs_mount(mp, path, data, ndp, p) register struct mount *mp; const char *path; void *data; struct nameidata *ndp; struct proc *p; { struct vnode *devvp; struct ufs_args args; struct ufsmount *ump = NULL; register struct fs *fs; int error = 0, flags; int ronly = 0; mode_t accessmode; size_t size; error = copyin(data, &args, sizeof (struct ufs_args)); if (error) return (error); #ifndef FFS_SOFTUPDATES if (mp->mnt_flag & MNT_SOFTDEP) { printf("WARNING: soft updates isn't compiled in\n"); mp->mnt_flag &= ~MNT_SOFTDEP; } #endif /* * Soft updates is incompatible with "async", * so if we are doing softupdates stop the user * from setting the async flag. */ if ((mp->mnt_flag & (MNT_SOFTDEP | MNT_ASYNC)) == (MNT_SOFTDEP | MNT_ASYNC)) { return (EINVAL); } /* * If updating, check whether changing from read-only to * read/write; if there is no device name, that's all we do. */ if (mp->mnt_flag & MNT_UPDATE) { ump = VFSTOUFS(mp); fs = ump->um_fs; devvp = ump->um_devvp; error = 0; ronly = fs->fs_ronly; if (ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) { flags = WRITECLOSE; if (mp->mnt_flag & MNT_FORCE) flags |= FORCECLOSE; if (fs->fs_flags & FS_DOSOFTDEP) { error = softdep_flushfiles(mp, flags, p); mp->mnt_flag &= ~MNT_SOFTDEP; } else error = ffs_flushfiles(mp, flags, p); ronly = 1; } /* * Flush soft dependencies if disabling it via an update * mount. This may leave some items to be processed, * so don't do this yet XXX. */ if ((fs->fs_flags & FS_DOSOFTDEP) && !(mp->mnt_flag & MNT_SOFTDEP) && !(mp->mnt_flag & MNT_RDONLY) && fs->fs_ronly == 0) { #if 0 flags = WRITECLOSE; if (mp->mnt_flag & MNT_FORCE) flags |= FORCECLOSE; error = softdep_flushfiles(mp, flags, p); #elif FFS_SOFTUPDATES mp->mnt_flag |= MNT_SOFTDEP; #endif } /* * When upgrading to a softdep mount, we must first flush * all vnodes. (not done yet -- see above) */ if (!(fs->fs_flags & FS_DOSOFTDEP) && (mp->mnt_flag & MNT_SOFTDEP) && fs->fs_ronly == 0) { #if 0 flags = WRITECLOSE; if (mp->mnt_flag & MNT_FORCE) flags |= FORCECLOSE; error = ffs_flushfiles(mp, flags, p); #else mp->mnt_flag &= ~MNT_SOFTDEP; #endif } if (!error && (mp->mnt_flag & MNT_RELOAD)) error = ffs_reload(mp, ndp->ni_cnd.cn_cred, p); if (error) goto error_1; if (ronly && (mp->mnt_flag & MNT_WANTRDWR)) { /* * If upgrade to read-write by non-root, then verify * that user has necessary permissions on the device. */ if (p->p_ucred->cr_uid != 0) { vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p); error = VOP_ACCESS(devvp, VREAD | VWRITE, p->p_ucred, p); VOP_UNLOCK(devvp, 0, p); if (error) goto error_1; } if (fs->fs_clean == 0) { #ifdef DIAGNOSTIC if (mp->mnt_flag & MNT_RELOAD) printf( "WARNING: %s still dirty after reload\n", fs->fs_fsmnt); #endif #if 0 /* * It is safe mount unclean filesystem * if it was previously mounted with softdep * but we may loss space and must * sometimes run fsck manually. */ if (fs->fs_flags & FS_DOSOFTDEP) printf( "WARNING: %s was not properly unmounted\n", fs->fs_fsmnt); else #endif if (mp->mnt_flag & MNT_FORCE) { printf( "WARNING: %s was not properly unmounted\n", fs->fs_fsmnt); } else { printf( "WARNING: R/W mount of %s denied. Filesystem is not clean - run fsck\n", fs->fs_fsmnt); error = EROFS; goto error_1; } } if ((fs->fs_flags & FS_DOSOFTDEP)) { error = softdep_mount(devvp, mp, fs, p->p_ucred); if (error) goto error_1; } fs->fs_contigdirs=(u_int8_t*)malloc((u_long)fs->fs_ncg, M_UFSMNT, M_WAITOK); bzero(fs->fs_contigdirs, fs->fs_ncg); ronly = 0; } if (args.fspec == 0) { /* * Process export requests. */ error = vfs_export(mp, &ump->um_export, &args.export_info); if (error) goto error_1; else goto success; } } /* * Not an update, or updating the name: look up the name * and verify that it refers to a sensible block device. */ NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p); if ((error = namei(ndp)) != 0) goto error_1; devvp = ndp->ni_vp; if (devvp->v_type != VBLK) { error = ENOTBLK; goto error_2; } if (major(devvp->v_rdev) >= nblkdev) { error = ENXIO; goto error_2; } /* * If mount by non-root, then verify that user has necessary * permissions on the device. */ if (p->p_ucred->cr_uid != 0) { accessmode = VREAD; if ((mp->mnt_flag & MNT_RDONLY) == 0) accessmode |= VWRITE; vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p); error = VOP_ACCESS(devvp, accessmode, p->p_ucred, p); VOP_UNLOCK(devvp, 0, p); if (error) goto error_2; } if (mp->mnt_flag & MNT_UPDATE) { /* * UPDATE * If it's not the same vnode, or at least the same device * then it's not correct. */ if (devvp != ump->um_devvp) { if (devvp->v_rdev == ump->um_devvp->v_rdev) { vrele(devvp); } else { error = EINVAL; /* needs translation */ } } else vrele(devvp); /* * Update device name only on success */ if (!error) { /* * Save "mounted from" info for mount point (NULL pad) */ copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); } } else { /* * Since this is a new mount, we want the names for * the device and the mount point copied in. If an * error occurs, the mountpoint is discarded by the * upper level code. */ /* Save "last mounted on" info for mount point (NULL pad)*/ copyinstr(path, /* mount point*/ mp->mnt_stat.f_mntonname, /* save area*/ MNAMELEN - 1, /* max size*/ &size); /* real size*/ bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); /* Save "mounted from" info for mount point (NULL pad)*/ copyinstr(args.fspec, /* device name*/ mp->mnt_stat.f_mntfromname, /* save area*/ MNAMELEN - 1, /* max size*/ &size); /* real size*/ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); error = ffs_mountfs(devvp, mp, p); } if (error) goto error_2; /* * Initialize FS stat information in mount struct; uses both * mp->mnt_stat.f_mntonname and mp->mnt_stat.f_mntfromname * * This code is common to root and non-root mounts */ bcopy(&args, &mp->mnt_stat.mount_info.ufs_args, sizeof(args)); (void)VFS_STATFS(mp, &mp->mnt_stat, p); success: if (path && (mp->mnt_flag & MNT_UPDATE)) { /* Update clean flag after changing read-onlyness. */ fs = ump->um_fs; if (ronly != fs->fs_ronly) { fs->fs_ronly = ronly; fs->fs_clean = ronly && (fs->fs_flags & FS_UNCLEAN) == 0 ? 1 : 0; if (ronly) free(fs->fs_contigdirs, M_UFSMNT); } if (!ronly) { if (mp->mnt_flag & MNT_SOFTDEP) fs->fs_flags |= FS_DOSOFTDEP; else fs->fs_flags &= ~FS_DOSOFTDEP; } /* add entropy, in case it's remount r/o */ arc4random_buf(fs->fs_historic_start, sizeof(fs->fs_historic_start)); ffs_sbupdate(ump, MNT_WAIT); } return (0); error_2: /* error with devvp held */ vrele (devvp); error_1: /* no state to back out */ return (error); } struct ffs_reload_args { struct fs *fs; struct proc *p; struct ucred *cred; struct vnode *devvp; }; int ffs_reload_vnode(struct vnode *vp, void *args) { struct ffs_reload_args *fra = args; struct inode *ip; struct buf *bp; int error; /* * Step 4: invalidate all inactive vnodes. */ if (vp->v_usecount == 0) { vgonel(vp, fra->p); return (0); } /* * Step 5: invalidate all cached file data. */ if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, fra->p)) return (0); if (vinvalbuf(vp, 0, fra->cred, fra->p, 0, 0)) panic("ffs_reload: dirty2"); /* * Step 6: re-read inode data for all active vnodes. */ ip = VTOI(vp); error = bread(fra->devvp, fsbtodb(fra->fs, ino_to_fsba(fra->fs, ip->i_number)), (int)fra->fs->fs_bsize, NOCRED, &bp); if (error) { vput(vp); return (error); } ip->i_din1 = *((struct ufs1_dinode *)bp->b_data + ino_to_fsbo(fra->fs, ip->i_number)); ip->i_effnlink = ip->i_ffs_nlink; brelse(bp); vput(vp); return (0); } /* * Reload all incore data for a filesystem (used after running fsck on * the root filesystem and finding things to fix). The filesystem must * be mounted read-only. * * Things to do to update the mount: * 1) invalidate all cached meta-data. * 2) re-read superblock from disk. * 3) re-read summary information from disk. * 4) invalidate all inactive vnodes. * 5) invalidate all cached file data. * 6) re-read inode data for all active vnodes. */ int ffs_reload(mountp, cred, p) register struct mount *mountp; struct ucred *cred; struct proc *p; { struct vnode *devvp; caddr_t space; struct fs *fs, *newfs; struct partinfo dpart; int i, blks, size, error; int32_t *lp; struct buf *bp = NULL; struct ffs_reload_args fra; if ((mountp->mnt_flag & MNT_RDONLY) == 0) return (EINVAL); /* * Step 1: invalidate all cached meta-data. */ devvp = VFSTOUFS(mountp)->um_devvp; vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p); error = vinvalbuf(devvp, 0, cred, p, 0, 0); VOP_UNLOCK(devvp, 0, p); if (error) panic("ffs_reload: dirty1"); /* * Step 2: re-read superblock from disk. */ if (VOP_IOCTL(devvp, DIOCGPART, (caddr_t)&dpart, FREAD, NOCRED, p) != 0) size = DEV_BSIZE; else size = dpart.disklab->d_secsize; error = bread(devvp, (daddr_t)(SBOFF / size), SBSIZE, NOCRED, &bp); if (error) return (error); newfs = (struct fs *)bp->b_data; if (newfs->fs_magic != FS_MAGIC || (u_int)newfs->fs_bsize > MAXBSIZE || newfs->fs_bsize < sizeof(struct fs) || (u_int)newfs->fs_sbsize > SBSIZE) { brelse(bp); return (EIO); /* XXX needs translation */ } fs = VFSTOUFS(mountp)->um_fs; /* * Copy pointer fields back into superblock before copying in XXX * new superblock. These should really be in the ufsmount. XXX * Note that important parameters (eg fs_ncg) are unchanged. */ newfs->fs_csp = fs->fs_csp; newfs->fs_maxcluster = fs->fs_maxcluster; newfs->fs_ronly = fs->fs_ronly; bcopy(newfs, fs, (u_int)fs->fs_sbsize); if (fs->fs_sbsize < SBSIZE) bp->b_flags |= B_INVAL; brelse(bp); mountp->mnt_maxsymlinklen = fs->fs_maxsymlinklen; ffs_oldfscompat(fs); (void)ffs_statfs(mountp, &mountp->mnt_stat, p); /* * Step 3: re-read summary information from disk. */ blks = howmany(fs->fs_cssize, fs->fs_fsize); space = (caddr_t)fs->fs_csp; for (i = 0; i < blks; i += fs->fs_frag) { size = fs->fs_bsize; if (i + fs->fs_frag > blks) size = (blks - i) * fs->fs_fsize; error = bread(devvp, fsbtodb(fs, fs->fs_csaddr + i), size, NOCRED, &bp); if (error) return (error); bcopy(bp->b_data, space, (u_int)size); space += size; brelse(bp); } if ((fs->fs_flags & FS_DOSOFTDEP)) (void) softdep_mount(devvp, mountp, fs, cred); /* * We no longer know anything about clusters per cylinder group. */ if (fs->fs_contigsumsize > 0) { lp = fs->fs_maxcluster; for (i = 0; i < fs->fs_ncg; i++) *lp++ = fs->fs_contigsumsize; } fra.p = p; fra.cred = cred; fra.fs = fs; fra.devvp = devvp; error = vfs_mount_foreach_vnode(mountp, ffs_reload_vnode, &fra); return (error); } /* * Common code for mount and mountroot */ int ffs_mountfs(devvp, mp, p) register struct vnode *devvp; struct mount *mp; struct proc *p; { register struct ufsmount *ump; struct buf *bp; register struct fs *fs; dev_t dev; struct partinfo dpart; caddr_t space; int error, i, blks, size, ronly; int32_t *lp; size_t strsize; struct ucred *cred; u_int64_t maxfilesize; /* XXX */ dev = devvp->v_rdev; cred = p ? p->p_ucred : NOCRED; /* * Disallow multiple mounts of the same device. * Disallow mounting of a device that is currently in use * (except for root, which might share swap device for miniroot). * Flush out any old buffers remaining from a previous use. */ if ((error = vfs_mountedon(devvp)) != 0) return (error); if (vcount(devvp) > 1 && devvp != rootvp) return (EBUSY); vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p); error = vinvalbuf(devvp, V_SAVE, cred, p, 0, 0); VOP_UNLOCK(devvp, 0, p); if (error) return (error); ronly = (mp->mnt_flag & MNT_RDONLY) != 0; error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p); if (error) return (error); if (VOP_IOCTL(devvp, DIOCGPART, (caddr_t)&dpart, FREAD, cred, p) != 0) size = DEV_BSIZE; else size = dpart.disklab->d_secsize; bp = NULL; ump = NULL; error = bread(devvp, (daddr_t)(SBOFF / size), SBSIZE, cred, &bp); if (error) goto out; fs = (struct fs *)bp->b_data; /* * MirOS ffs specific: add true (first two) and pseudo (last) * randomness from superblock */ rnd_lopool_add(fs->fs_historic_start, sizeof(fs->fs_historic_start)); rnd_lopool_addh(fs, SBSIZE); arc4random_buf(fs->fs_historic_start, sizeof(fs->fs_historic_start)); if (fs->fs_magic != FS_UFS1_MAGIC || (u_int)fs->fs_bsize > MAXBSIZE || fs->fs_bsize < sizeof(struct fs) || (u_int)fs->fs_sbsize > SBSIZE) { if (fs->fs_magic == FS_UFS2_MAGIC) printf("no UFS2 support\n"); error = EFTYPE; /* Inappropriate format */ goto out; } fs->fs_fmod = 0; fs->fs_flags &= ~FS_UNCLEAN; if (fs->fs_clean == 0) { fs->fs_flags |= FS_UNCLEAN; #if 0 /* * It is safe mount unclean filesystem * if it was previously mounted with softdep * but we may loss space and must * sometimes run fsck manually. */ if (fs->fs_flags & FS_DOSOFTDEP) printf( "WARNING: %s was not properly unmounted\n", fs->fs_fsmnt); else #endif if (ronly || (mp->mnt_flag & MNT_FORCE)) { printf( "WARNING: %s was not properly unmounted\n", fs->fs_fsmnt); } else { printf( "WARNING: R/W mount of %s denied. Filesystem is not clean - run fsck\n", fs->fs_fsmnt); error = EROFS; goto out; } } /* XXX updating 4.2 FFS superblocks trashes rotational layout tables */ if (fs->fs_postblformat == FS_42POSTBLFMT && !ronly) { error = EROFS; /* XXX what should be returned? */ goto out; } ump = malloc(sizeof *ump, M_UFSMNT, M_WAITOK); bzero(ump, sizeof *ump); ump->um_fs = malloc((u_long)fs->fs_sbsize, M_UFSMNT, M_WAITOK); if (fs->fs_magic == FS_UFS1_MAGIC) { ump->um_fstype = UM_UFS1; } bcopy(bp->b_data, ump->um_fs, (u_int)fs->fs_sbsize); if (fs->fs_sbsize < SBSIZE) bp->b_flags |= B_INVAL; brelse(bp); bp = NULL; fs = ump->um_fs; fs->fs_ronly = ronly; size = fs->fs_cssize; blks = howmany(size, fs->fs_fsize); if (fs->fs_contigsumsize > 0) size += fs->fs_ncg * sizeof(int32_t); space = malloc((u_long)size, M_UFSMNT, M_WAITOK); fs->fs_csp = (struct csum *)space; for (i = 0; i < blks; i += fs->fs_frag) { size = fs->fs_bsize; if (i + fs->fs_frag > blks) size = (blks - i) * fs->fs_fsize; error = bread(devvp, fsbtodb(fs, fs->fs_csaddr + i), size, cred, &bp); if (error) { free(fs->fs_csp, M_UFSMNT); goto out; } bcopy(bp->b_data, space, (u_int)size); space += size; brelse(bp); bp = NULL; } if (fs->fs_contigsumsize > 0) { fs->fs_maxcluster = lp = (int32_t *)space; for (i = 0; i < fs->fs_ncg; i++) *lp++ = fs->fs_contigsumsize; } mp->mnt_data = (qaddr_t)ump; mp->mnt_stat.f_fsid.val[0] = (long)dev; /* Use on-disk fsid if it exists, else fake it */ if (fs->fs_id[0] != 0 && fs->fs_id[1] != 0) mp->mnt_stat.f_fsid.val[1] = fs->fs_id[1]; else mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; mp->mnt_maxsymlinklen = fs->fs_maxsymlinklen; mp->mnt_flag |= MNT_LOCAL; ump->um_mountp = mp; ump->um_dev = dev; ump->um_devvp = devvp; ump->um_nindir = fs->fs_nindir; ump->um_bptrtodb = fs->fs_fsbtodb; ump->um_seqinc = fs->fs_frag; for (i = 0; i < MAXQUOTAS; i++) ump->um_quotas[i] = NULLVP; devvp->v_specmountpoint = mp; ffs_oldfscompat(fs); if (ronly) fs->fs_contigdirs = NULL; else { fs->fs_contigdirs = (u_int8_t*)malloc((u_long)fs->fs_ncg, M_UFSMNT, M_WAITOK); bzero(fs->fs_contigdirs, fs->fs_ncg); } /* * Set FS local "last mounted on" information (NULL pad) */ copystr(mp->mnt_stat.f_mntonname, /* mount point*/ fs->fs_fsmnt, /* copy area*/ sizeof(fs->fs_fsmnt) - 1, /* max size*/ &strsize); /* real size*/ bzero(fs->fs_fsmnt + strsize, sizeof(fs->fs_fsmnt) - strsize); #if 0 if( mp->mnt_flag & MNT_ROOTFS) { /* * Root mount; update timestamp in mount structure. * this will be used by the common root mount code * to update the system clock. */ mp->mnt_time = fs->fs_time; } #endif /* * XXX * Limit max file size. Even though ffs can handle files up to 16TB, * we do limit the max file to 2^31 pages to prevent overflow of * a 32-bit unsigned int. The buffer cache has its own checks but * a little added paranoia never hurts. */ ump->um_savedmaxfilesize = fs->fs_maxfilesize; /* XXX */ maxfilesize = (u_int64_t)0x80000000 * MIN(PAGE_SIZE, fs->fs_bsize) - 1; if (fs->fs_maxfilesize > maxfilesize) /* XXX */ fs->fs_maxfilesize = maxfilesize; /* XXX */ if (ronly == 0) { if ((fs->fs_flags & FS_DOSOFTDEP) && (error = softdep_mount(devvp, mp, fs, cred)) != 0) { free(fs->fs_csp, M_UFSMNT); free(fs->fs_contigdirs, M_UFSMNT); goto out; } fs->fs_fmod = 1; fs->fs_clean = 0; if (mp->mnt_flag & MNT_SOFTDEP) fs->fs_flags |= FS_DOSOFTDEP; else fs->fs_flags &= ~FS_DOSOFTDEP; (void) ffs_sbupdate(ump, MNT_WAIT); } return (0); out: devvp->v_specmountpoint = NULL; if (bp) brelse(bp); (void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, cred, p); if (ump) { free(ump->um_fs, M_UFSMNT); free(ump, M_UFSMNT); mp->mnt_data = (qaddr_t)0; } return (error); } /* * Sanity checks for old filesystems. * * XXX - goes away some day. */ int ffs_oldfscompat(fs) struct fs *fs; { int i; fs->fs_npsect = max(fs->fs_npsect, fs->fs_nsect); /* XXX */ fs->fs_interleave = max(fs->fs_interleave, 1); /* XXX */ if (fs->fs_postblformat == FS_42POSTBLFMT) /* XXX */ fs->fs_nrpos = 8; /* XXX */ if (fs->fs_inodefmt < FS_44INODEFMT) { /* XXX */ u_int64_t sizepb = fs->fs_bsize; /* XXX */ /* XXX */ fs->fs_maxfilesize = fs->fs_bsize * NDADDR - 1; /* XXX */ for (i = 0; i < NIADDR; i++) { /* XXX */ sizepb *= NINDIR(fs); /* XXX */ fs->fs_maxfilesize += sizepb; /* XXX */ } /* XXX */ fs->fs_qbmask = ~fs->fs_bmask; /* XXX */ fs->fs_qfmask = ~fs->fs_fmask; /* XXX */ } /* XXX */ if (fs->fs_avgfilesize <= 0) /* XXX */ fs->fs_avgfilesize = AVFILESIZ; /* XXX */ if (fs->fs_avgfpdir <= 0) /* XXX */ fs->fs_avgfpdir = AFPDIR; /* XXX */ return (0); } /* * unmount system call */ int ffs_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { register struct ufsmount *ump; register struct fs *fs; int error, flags; flags = 0; if (mntflags & MNT_FORCE) flags |= FORCECLOSE; ump = VFSTOUFS(mp); fs = ump->um_fs; /* MirOS specific: write random data into superblock (entropy seed) */ arc4random_buf(fs->fs_historic_start, sizeof(fs->fs_historic_start)); if (mp->mnt_flag & MNT_SOFTDEP) error = softdep_flushfiles(mp, flags, p); else error = ffs_flushfiles(mp, flags, p); if (error != 0) return (error); if (fs->fs_ronly == 0) { fs->fs_clean = (fs->fs_flags & FS_UNCLEAN) ? 0 : 1; error = ffs_sbupdate(ump, MNT_WAIT); if (error) { fs->fs_clean = 0; return (error); } free(fs->fs_contigdirs, M_UFSMNT); } ump->um_devvp->v_specmountpoint = NULL; vinvalbuf(ump->um_devvp, V_SAVE, NOCRED, p, 0, 0); error = VOP_CLOSE(ump->um_devvp, fs->fs_ronly ? FREAD : FREAD|FWRITE, NOCRED, p); vrele(ump->um_devvp); free(fs->fs_csp, M_UFSMNT); free(fs, M_UFSMNT); free(ump, M_UFSMNT); mp->mnt_data = (qaddr_t)0; mp->mnt_flag &= ~MNT_LOCAL; return (error); } /* * Flush out all the files in a filesystem. */ int ffs_flushfiles(mp, flags, p) register struct mount *mp; int flags; struct proc *p; { register struct ufsmount *ump; int error; ump = VFSTOUFS(mp); if (mp->mnt_flag & MNT_QUOTA) { int i; if ((error = vflush(mp, NULLVP, SKIPSYSTEM|flags)) != 0) return (error); for (i = 0; i < MAXQUOTAS; i++) { if (ump->um_quotas[i] == NULLVP) continue; quotaoff(p, mp, i); } /* * Here we fall through to vflush again to ensure * that we have gotten rid of all the system vnodes. */ } /* * Flush all the files. */ if ((error = vflush(mp, NULL, flags)) != 0) return (error); /* * Flush filesystem metadata. */ vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY, p); error = VOP_FSYNC(ump->um_devvp, p->p_ucred, MNT_WAIT, p); VOP_UNLOCK(ump->um_devvp, 0, p); return (error); } /* * Get filesystem statistics. */ int ffs_statfs(mp, sbp, p) struct mount *mp; register struct statfs *sbp; struct proc *p; { register struct ufsmount *ump; register struct fs *fs; ump = VFSTOUFS(mp); fs = ump->um_fs; if (fs->fs_magic != FS_MAGIC) panic("ffs_statfs"); sbp->f_bsize = fs->fs_fsize; sbp->f_iosize = fs->fs_bsize; sbp->f_blocks = fs->fs_dsize; sbp->f_bfree = fs->fs_cstotal.cs_nbfree * fs->fs_frag + fs->fs_cstotal.cs_nffree; sbp->f_bavail = sbp->f_bfree - ((int64_t)fs->fs_dsize * fs->fs_minfree / 100); sbp->f_files = fs->fs_ncg * fs->fs_ipg - ROOTINO; sbp->f_ffree = fs->fs_cstotal.cs_nifree; if (sbp != &mp->mnt_stat) { bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); bcopy(&mp->mnt_stat.mount_info.ufs_args, &sbp->mount_info.ufs_args, sizeof(struct ufs_args)); } strncpy(sbp->f_fstypename, mp->mnt_vfc->vfc_name, MFSNAMELEN); return (0); } struct ffs_sync_args { int allerror; struct proc *p; int waitfor; struct ucred *cred; }; int ffs_sync_vnode(struct vnode *vp, void *arg) { struct ffs_sync_args *fsa = arg; struct inode *ip; int error; ip = VTOI(vp); if (fsa->waitfor == MNT_LAZY || vp->v_type == VNON || ((ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0 && LIST_EMPTY(&vp->v_dirtyblkhd)) ) { simple_unlock(&vp->v_interlock); return (0); } if (vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, fsa->p)) return (0); if ((error = VOP_FSYNC(vp, fsa->cred, fsa->waitfor, fsa->p))) fsa->allerror = error; VOP_UNLOCK(vp, 0, fsa->p); vrele(vp); return (0); } /* * Go through the disk queues to initiate sandbagged IO; * go through the inodes to write those that have been modified; * initiate the writing of the super block if it has been modified. * * Note: we are always called with the filesystem marked 'MPBUSY'. */ int ffs_sync(mp, waitfor, cred, p) struct mount *mp; int waitfor; struct ucred *cred; struct proc *p; { struct ufsmount *ump = VFSTOUFS(mp); struct fs *fs; int error, allerror = 0, count; struct ffs_sync_args fsa; fs = ump->um_fs; /* * Write back modified superblock. * Consistency check that the superblock * is still in the buffer cache. */ if (fs->fs_fmod != 0 && fs->fs_ronly != 0) { printf("fs = %s\n", fs->fs_fsmnt); panic("update: rofs mod"); } loop: /* * Write back each (modified) inode. */ fsa.allerror = 0; fsa.p = p; fsa.cred = cred; fsa.waitfor = waitfor; vfs_mount_foreach_vnode(mp, ffs_sync_vnode, &fsa); if (fsa.allerror != 0) allerror = fsa.allerror; /* * Force stale filesystem control information to be flushed. */ if ((ump->um_mountp->mnt_flag & MNT_SOFTDEP) && waitfor == MNT_WAIT) { if ((error = softdep_flushworklist(ump->um_mountp, &count, p))) allerror = error; /* Flushed work items may create new vnodes to clean */ if (count) goto loop; } if (waitfor != MNT_LAZY) { if (ump->um_mountp->mnt_flag & MNT_SOFTDEP) waitfor = MNT_NOWAIT; vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY, p); if ((error = VOP_FSYNC(ump->um_devvp, cred, waitfor, p)) != 0) allerror = error; VOP_UNLOCK(ump->um_devvp, 0, p); } qsync(mp); /* * Write back modified superblock. */ if (fs->fs_fmod != 0 && (error = ffs_sbupdate(ump, waitfor)) != 0) allerror = error; return (allerror); } /* * Look up a FFS dinode number to find its incore vnode, otherwise read it * in from disk. If it is in core, wait for the lock bit to clear, then * return the inode locked. Detection and handling of mount points must be * done by the calling routine. */ int ffs_vget(mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { register struct fs *fs; register struct inode *ip; struct ufsmount *ump; struct buf *bp; struct vnode *vp; dev_t dev; int error; ump = VFSTOUFS(mp); dev = ump->um_dev; retry: if ((*vpp = ufs_ihashget(dev, ino)) != NULL) return (0); /* Allocate a new vnode/inode. */ if ((error = getnewvnode(VT_UFS, mp, ffs_vnodeop_p, &vp)) != 0) { *vpp = NULL; return (error); } #ifdef VFSDEBUG vp->v_flag |= VLOCKSWORK; #endif /* XXX - we use the same pool for ffs and mfs */ ip = pool_get(&ffs_ino_pool, PR_WAITOK); bzero((caddr_t)ip, sizeof(struct inode)); lockinit(&ip->i_lock, PINOD, "inode", 0, 0); ip->i_ump = ump; VREF(ip->i_devvp); vp->v_data = ip; ip->i_vnode = vp; ip->i_fs = fs = ump->um_fs; ip->i_dev = dev; ip->i_number = ino; ip->i_vtbl = &ffs_vtbl; /* * Put it onto its hash chain and lock it so that other requests for * this inode will block if they arrive while we are sleeping waiting * for old data structures to be purged or for the contents of the * disk portion of this inode to be read. */ error = ufs_ihashins(ip); if (error) { /* * VOP_INACTIVE will treat this as a stale file * and recycle it quickly */ vrele(vp); if (error == EEXIST) goto retry; return (error); } /* Read in the disk contents for the inode, copy into the inode. */ error = bread(ump->um_devvp, fsbtodb(fs, ino_to_fsba(fs, ino)), (int)fs->fs_bsize, NOCRED, &bp); if (error) { /* * The inode does not contain anything useful, so it would * be misleading to leave it on its hash chain. With mode * still zero, it will be unlinked and returned to the free * list by vput(). */ vput(vp); brelse(bp); *vpp = NULL; return (error); } ip->i_din1 = *((struct ufs1_dinode *)bp->b_data + ino_to_fsbo(fs, ino)); if (DOINGSOFTDEP(vp)) softdep_load_inodeblock(ip); else ip->i_effnlink = ip->i_ffs_nlink; brelse(bp); /* * Initialize the vnode from the inode, check for aliases. * Note that the underlying vnode may have changed. */ error = ufs_vinit(mp, ffs_specop_p, FFS_FIFOOPS, &vp); if (error) { vput(vp); *vpp = NULL; return (error); } /* * Set up a generation number for this inode if it does not * already have one. This should only happen on old filesystems. */ if (ip->i_ffs_gen == 0) { ip->i_ffs_gen = arc4random() & INT_MAX; if (ip->i_ffs_gen == 0 || ip->i_ffs_gen == -1) ip->i_ffs_gen = 1; /* shouldn't happen */ if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) ip->i_flag |= IN_MODIFIED; } /* * Ensure that uid and gid are correct. This is a temporary * fix until fsck has been changed to do the update. */ if (fs->fs_inodefmt < FS_44INODEFMT) { /* XXX */ ip->i_ffs_uid = ip->i_din1.di_ouid; /* XXX */ ip->i_ffs_gid = ip->i_din1.di_ogid; /* XXX */ } /* XXX */ *vpp = vp; return (0); } /* * File handle to vnode * * Have to be really careful about stale file handles: * - check that the inode number is valid * - call ffs_vget() to get the locked inode * - check for an unallocated inode (i_mode == 0) */ int ffs_fhtovp(mp, fhp, vpp) register struct mount *mp; struct fid *fhp; struct vnode **vpp; { register struct ufid *ufhp; struct fs *fs; ufhp = (struct ufid *)fhp; fs = VFSTOUFS(mp)->um_fs; if (ufhp->ufid_ino < ROOTINO || ufhp->ufid_ino >= fs->fs_ncg * fs->fs_ipg) return (ESTALE); return (ufs_fhtovp(mp, ufhp, vpp)); } /* * Vnode pointer to File handle */ /* ARGSUSED */ int ffs_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { register struct inode *ip; register struct ufid *ufhp; ip = VTOI(vp); ufhp = (struct ufid *)fhp; ufhp->ufid_len = sizeof(struct ufid); ufhp->ufid_ino = ip->i_number; ufhp->ufid_gen = ip->i_ffs_gen; return (0); } /* * Write a superblock and associated information back to disk. */ int ffs_sbupdate(mp, waitfor) struct ufsmount *mp; int waitfor; { register struct fs *dfs, *fs = mp->um_fs; register struct buf *bp; int blks; caddr_t space; int i, size, error, allerror = 0; /* * First write back the summary information. */ blks = howmany(fs->fs_cssize, fs->fs_fsize); space = (caddr_t)fs->fs_csp; for (i = 0; i < blks; i += fs->fs_frag) { size = fs->fs_bsize; if (i + fs->fs_frag > blks) size = (blks - i) * fs->fs_fsize; bp = getblk(mp->um_devvp, fsbtodb(fs, fs->fs_csaddr + i), size, 0, 0); bcopy(space, bp->b_data, (u_int)size); space += size; if (waitfor != MNT_WAIT) bawrite(bp); else if ((error = bwrite(bp))) allerror = error; } /* * Now write back the superblock itself. If any errors occurred * up to this point, then fail so that the superblock avoids * being written out as clean. */ if (allerror) return (allerror); bp = getblk(mp->um_devvp, SBOFF >> (fs->fs_fshift - fs->fs_fsbtodb), (int)fs->fs_sbsize, 0, 0); fs->fs_fmod = 0; fs->fs_time = time.tv_sec; bcopy((caddr_t)fs, bp->b_data, (u_int)fs->fs_sbsize); /* Restore compatibility to old filesystems. XXX */ dfs = (struct fs *)bp->b_data; /* XXX */ if (fs->fs_postblformat == FS_42POSTBLFMT) /* XXX */ dfs->fs_nrpos = -1; /* XXX */ if (fs->fs_inodefmt < FS_44INODEFMT) { /* XXX */ int32_t *lp, tmp; /* XXX */ /* XXX */ lp = (int32_t *)&dfs->fs_qbmask; /* XXX */ tmp = lp[4]; /* XXX */ for (i = 4; i > 0; i--) /* XXX */ lp[i] = lp[i-1]; /* XXX */ lp[0] = tmp; /* XXX */ } /* XXX */ dfs->fs_maxfilesize = mp->um_savedmaxfilesize; /* XXX */ if (waitfor != MNT_WAIT) bawrite(bp); else if ((error = bwrite(bp))) allerror = error; return (allerror); } int ffs_init(vfsp) struct vfsconf *vfsp; { static int done; if (done) return (0); done = 1; pool_init(&ffs_ino_pool, sizeof(struct inode), 0, 0, 0, "ffsino", &pool_allocator_nointr); softdep_initialize(); return (ufs_init(vfsp)); } /* * fast filesystem related variables. */ int ffs_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) int *name; u_int namelen; void *oldp; size_t *oldlenp; void *newp; size_t newlen; struct proc *p; { extern int doclusterread, doclusterwrite, doreallocblks, doasyncfree; #ifdef FFS_SOFTUPDATES extern int max_softdeps, tickdelay, stat_worklist_push; extern int stat_blk_limit_push, stat_ino_limit_push, stat_blk_limit_hit; extern int stat_ino_limit_hit, stat_sync_limit_hit, stat_indir_blk_ptrs; extern int stat_inode_bitmap, stat_direct_blk_ptrs, stat_dir_entry; #endif /* all sysctl names at this level are terminal */ if (namelen != 1) return (ENOTDIR); /* overloaded */ switch (name[0]) { case FFS_CLUSTERREAD: return (sysctl_int(oldp, oldlenp, newp, newlen, &doclusterread)); case FFS_CLUSTERWRITE: return (sysctl_int(oldp, oldlenp, newp, newlen, &doclusterwrite)); case FFS_REALLOCBLKS: return (sysctl_int(oldp, oldlenp, newp, newlen, &doreallocblks)); case FFS_ASYNCFREE: return (sysctl_int(oldp, oldlenp, newp, newlen, &doasyncfree)); #ifdef FFS_SOFTUPDATES case FFS_MAX_SOFTDEPS: return (sysctl_int(oldp, oldlenp, newp, newlen, &max_softdeps)); case FFS_SD_TICKDELAY: return (sysctl_int(oldp, oldlenp, newp, newlen, &tickdelay)); case FFS_SD_WORKLIST_PUSH: return (sysctl_rdint(oldp, oldlenp, newp, stat_worklist_push)); case FFS_SD_BLK_LIMIT_PUSH: return (sysctl_rdint(oldp, oldlenp, newp, stat_blk_limit_push)); case FFS_SD_INO_LIMIT_PUSH: return (sysctl_rdint(oldp, oldlenp, newp, stat_ino_limit_push)); case FFS_SD_BLK_LIMIT_HIT: return (sysctl_rdint(oldp, oldlenp, newp, stat_blk_limit_hit)); case FFS_SD_INO_LIMIT_HIT: return (sysctl_rdint(oldp, oldlenp, newp, stat_ino_limit_hit)); case FFS_SD_SYNC_LIMIT_HIT: return (sysctl_rdint(oldp, oldlenp, newp, stat_sync_limit_hit)); case FFS_SD_INDIR_BLK_PTRS: return (sysctl_rdint(oldp, oldlenp, newp, stat_indir_blk_ptrs)); case FFS_SD_INODE_BITMAP: return (sysctl_rdint(oldp, oldlenp, newp, stat_inode_bitmap)); case FFS_SD_DIRECT_BLK_PTRS: return (sysctl_rdint(oldp, oldlenp, newp, stat_direct_blk_ptrs)); case FFS_SD_DIR_ENTRY: return (sysctl_rdint(oldp, oldlenp, newp, stat_dir_entry)); #endif #ifdef UFS_DIRHASH case FFS_DIRHASH_DIRSIZE: return (sysctl_int(oldp, oldlenp, newp, newlen, &ufs_mindirhashsize)); case FFS_DIRHASH_MAXMEM: return (sysctl_int(oldp, oldlenp, newp, newlen, &ufs_dirhashmaxmem)); case FFS_DIRHASH_MEM: return (sysctl_rdint(oldp, oldlenp, newp, ufs_dirhashmem)); #endif default: return (EOPNOTSUPP); } /* NOTREACHED */ } makefs/src/sys/ufs/ffs/ffs_vnops.c010064400000000000000000000241521026206255400144100ustar00/* $OpenBSD: ffs_vnops.c,v 1.33 2005/05/28 02:02:50 pedro Exp $ */ /* $NetBSD: ffs_vnops.c,v 1.7 1996/05/11 18:27:24 mycroft Exp $ */ /* * Copyright (c) 1982, 1986, 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. * * @(#)ffs_vnops.c 8.10 (Berkeley) 8/10/94 */ #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 /* Global vfs data structures for ufs. */ int (**ffs_vnodeop_p)(void *); struct vnodeopv_entry_desc ffs_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, ufs_lookup }, /* lookup */ { &vop_create_desc, ufs_create }, /* create */ { &vop_mknod_desc, ufs_mknod }, /* mknod */ { &vop_open_desc, ufs_open }, /* open */ { &vop_close_desc, ufs_close }, /* close */ { &vop_access_desc, ufs_access }, /* access */ { &vop_getattr_desc, ufs_getattr }, /* getattr */ { &vop_setattr_desc, ufs_setattr }, /* setattr */ { &vop_read_desc, ffs_read }, /* read */ { &vop_write_desc, ffs_write }, /* write */ { &vop_lease_desc, ufs_lease_check }, /* lease */ { &vop_ioctl_desc, ufs_ioctl }, /* ioctl */ { &vop_poll_desc, ufs_poll }, /* poll */ { &vop_kqfilter_desc, ufs_kqfilter }, /* kqfilter */ { &vop_revoke_desc, ufs_revoke }, /* revoke */ { &vop_fsync_desc, ffs_fsync }, /* fsync */ { &vop_remove_desc, ufs_remove }, /* remove */ { &vop_link_desc, ufs_link }, /* link */ { &vop_rename_desc, ufs_rename }, /* rename */ { &vop_mkdir_desc, ufs_mkdir }, /* mkdir */ { &vop_rmdir_desc, ufs_rmdir }, /* rmdir */ { &vop_symlink_desc, ufs_symlink }, /* symlink */ { &vop_readdir_desc, ufs_readdir }, /* readdir */ { &vop_readlink_desc, ufs_readlink }, /* readlink */ { &vop_abortop_desc, vop_generic_abortop }, /* abortop */ { &vop_inactive_desc, ufs_inactive }, /* inactive */ { &vop_reclaim_desc, ffs_reclaim }, /* reclaim */ { &vop_lock_desc, ufs_lock }, /* lock */ { &vop_unlock_desc, ufs_unlock }, /* unlock */ { &vop_bmap_desc, ufs_bmap }, /* bmap */ { &vop_strategy_desc, ufs_strategy }, /* strategy */ { &vop_print_desc, ufs_print }, /* print */ { &vop_islocked_desc, ufs_islocked }, /* islocked */ { &vop_pathconf_desc, ufs_pathconf }, /* pathconf */ { &vop_advlock_desc, ufs_advlock }, /* advlock */ { &vop_reallocblks_desc, ffs_reallocblks }, /* reallocblks */ { &vop_bwrite_desc, vop_generic_bwrite }, #ifdef UFS_EXTATTR { &vop_getextattr_desc, ufs_vop_getextattr }, { &vop_setextattr_desc, ufs_vop_setextattr }, #endif { NULL, NULL } }; struct vnodeopv_desc ffs_vnodeop_opv_desc = { &ffs_vnodeop_p, ffs_vnodeop_entries }; int (**ffs_specop_p)(void *); struct vnodeopv_entry_desc ffs_specop_entries[] = { { &vop_default_desc, spec_vnoperate }, { &vop_close_desc, ufsspec_close }, /* close */ { &vop_access_desc, ufs_access }, /* access */ { &vop_getattr_desc, ufs_getattr }, /* getattr */ { &vop_setattr_desc, ufs_setattr }, /* setattr */ { &vop_read_desc, ufsspec_read }, /* read */ { &vop_write_desc, ufsspec_write }, /* write */ { &vop_fsync_desc, ffs_fsync }, /* fsync */ { &vop_inactive_desc, ufs_inactive }, /* inactive */ { &vop_reclaim_desc, ffs_reclaim }, /* reclaim */ { &vop_lock_desc, ufs_lock }, /* lock */ { &vop_unlock_desc, ufs_unlock }, /* unlock */ { &vop_print_desc, ufs_print }, /* print */ { &vop_islocked_desc, ufs_islocked }, /* islocked */ #ifdef UFS_EXTATTR { &vop_getextattr_desc, ufs_vop_getextattr }, { &vop_setextattr_desc, ufs_vop_setextattr }, #endif { NULL, NULL } }; struct vnodeopv_desc ffs_specop_opv_desc = { &ffs_specop_p, ffs_specop_entries }; #ifdef FIFO int (**ffs_fifoop_p)(void *); struct vnodeopv_entry_desc ffs_fifoop_entries[] = { { &vop_default_desc, fifo_vnoperate }, { &vop_close_desc, ufsfifo_close }, /* close */ { &vop_access_desc, ufs_access }, /* access */ { &vop_getattr_desc, ufs_getattr }, /* getattr */ { &vop_setattr_desc, ufs_setattr }, /* setattr */ { &vop_read_desc, ufsfifo_read }, /* read */ { &vop_write_desc, ufsfifo_write }, /* write */ { &vop_fsync_desc, ffs_fsync }, /* fsync */ { &vop_inactive_desc, ufs_inactive }, /* inactive */ { &vop_reclaim_desc, ffsfifo_reclaim }, /* reclaim */ { &vop_lock_desc, ufs_lock }, /* lock */ { &vop_unlock_desc, ufs_unlock }, /* unlock */ { &vop_print_desc, ufs_print }, /* print */ { &vop_islocked_desc, ufs_islocked }, /* islocked */ { &vop_bwrite_desc, vop_generic_bwrite }, #ifdef UFS_EXTATTR { &vop_getextattr_desc, ufs_vop_getextattr }, { &vop_setextattr_desc, ufs_vop_setextattr }, #endif { NULL, NULL } }; struct vnodeopv_desc ffs_fifoop_opv_desc = { &ffs_fifoop_p, ffs_fifoop_entries }; #endif /* FIFO */ /* * Enabling cluster read/write operations. */ int doclusterread = 1; int doclusterwrite = 1; #include /* * Synch an open file. */ /* ARGSUSED */ int ffs_fsync(v) void *v; { struct vop_fsync_args /* { struct vnode *a_vp; struct ucred *a_cred; int a_waitfor; struct proc *a_p; } */ *ap = v; struct vnode *vp = ap->a_vp; struct buf *bp, *nbp; int s, error, passes, skipmeta; if (vp->v_type == VBLK && vp->v_specmountpoint != NULL && (vp->v_specmountpoint->mnt_flag & MNT_SOFTDEP)) softdep_fsync_mountdev(vp); /* * Flush all dirty buffers associated with a vnode. */ passes = NIADDR + 1; skipmeta = 0; if (ap->a_waitfor == MNT_WAIT) skipmeta = 1; s = splbio(); loop: for (bp = LIST_FIRST(&vp->v_dirtyblkhd); bp; bp = LIST_NEXT(bp, b_vnbufs)) bp->b_flags &= ~B_SCANNED; for (bp = LIST_FIRST(&vp->v_dirtyblkhd); bp; bp = nbp) { nbp = LIST_NEXT(bp, b_vnbufs); /* * Reasons to skip this buffer: it has already been considered * on this pass, this pass is the first time through on a * synchronous flush request and the buffer being considered * is metadata, the buffer has dependencies that will cause * it to be redirtied and it has not already been deferred, * or it is already being written. */ if (bp->b_flags & (B_BUSY | B_SCANNED)) continue; if ((bp->b_flags & B_DELWRI) == 0) panic("ffs_fsync: not dirty"); if (skipmeta && bp->b_lblkno < 0) continue; if (ap->a_waitfor != MNT_WAIT && LIST_FIRST(&bp->b_dep) != NULL && (bp->b_flags & B_DEFERRED) == 0 && buf_countdeps(bp, 0, 1)) { bp->b_flags |= B_DEFERRED; continue; } bremfree(bp); bp->b_flags |= B_BUSY | B_SCANNED; splx(s); /* * On our final pass through, do all I/O synchronously * so that we can find out if our flush is failing * because of write errors. */ if (passes > 0 || ap->a_waitfor != MNT_WAIT) (void) bawrite(bp); else if ((error = bwrite(bp)) != 0) return (error); s = splbio(); /* * Since we may have slept during the I/O, we need * to start from a known point. */ nbp = LIST_FIRST(&vp->v_dirtyblkhd); } if (skipmeta) { skipmeta = 0; goto loop; } if (ap->a_waitfor == MNT_WAIT) { vwaitforio(vp, 0, "ffs_fsync", 0); /* * Ensure that any filesystem metadata associated * with the vnode has been written. */ splx(s); if ((error = softdep_sync_metadata(ap)) != 0) return (error); s = splbio(); if (!LIST_EMPTY(&vp->v_dirtyblkhd)) { /* * Block devices associated with filesystems may * have new I/O requests posted for them even if * the vnode is locked, so no amount of trying will * get them clean. Thus we give block devices a * good effort, then just give up. For all other file * types, go around and try again until it is clean. */ if (passes > 0) { passes -= 1; goto loop; } #ifdef DIAGNOSTIC if (vp->v_type != VBLK) vprint("ffs_fsync: dirty", vp); #endif } } splx(s); return (UFS_UPDATE(VTOI(vp), ap->a_waitfor == MNT_WAIT)); } /* * Reclaim an inode so that it can be used for other purposes. */ int ffs_reclaim(v) void *v; { struct vop_reclaim_args /* { struct vnode *a_vp; struct proc *a_p; } */ *ap = v; register struct vnode *vp = ap->a_vp; int error; if ((error = ufs_reclaim(vp, ap->a_p)) != 0) return (error); /* XXX - same for for both mfs and ffs */ pool_put(&ffs_ino_pool, vp->v_data); vp->v_data = NULL; return (0); } #ifdef FIFO int ffsfifo_reclaim(void *v) { fifo_reclaim(v); return (ffs_reclaim(v)); } #endif makefs/src/sys/ufs/ffs/fs.h010064400000000000000000000557311314214547300130330ustar00/** $MirOS: src/sys/ufs/ffs/fs.h,v 1.8 2017/08/07 20:18:40 tg Exp $ */ /* $OpenBSD: fs.h,v 1.17 2005/03/01 13:30:50 aaron Exp $ */ /* $NetBSD: fs.h,v 1.6 1995/04/12 21:21:02 mycroft Exp $ */ /* * Copyright (c) 1982, 1986, 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. * * @(#)fs.h 8.10 (Berkeley) 10/27/94 */ #ifndef UFS_FFS_FS_H #define UFS_FFS_FS_H /* * Each disk drive contains some number of filesystems. * A filesystem consists of a number of cylinder groups. * Each cylinder group has inodes and data. * * A filesystem is described by its super-block, which in turn * describes the cylinder groups. The super-block is critical * data and is replicated in each cylinder group to protect against * catastrophic loss. This is done at `newfs' time and the critical * super-block data does not change, so the copies need not be * referenced further unless disaster strikes. * * For filesystem fs, the offsets of the various blocks of interest * are given in the super block as: * [fs->fs_sblkno] Super-block * [fs->fs_cblkno] Cylinder group block * [fs->fs_iblkno] Inode blocks * [fs->fs_dblkno] Data blocks * The beginning of cylinder group cg in fs, is given by * the ``cgbase(fs, cg)'' macro. * * The first boot and super blocks are given in absolute disk addresses. * The byte-offset forms are preferred, as they don't imply a sector size. */ #define BBSIZE 8192 #define SBSIZE 8192 #define BBOFF ((off_t)(0)) #define SBOFF ((off_t)(BBOFF + BBSIZE)) #define BBLOCK ((daddr_t)(0)) #define SBLOCK ((daddr_t)(BBLOCK + BBSIZE / DEV_BSIZE)) #define SBLOCK_FLOPPY 0 #define SBLOCK_UFS1 8192 #define SBLOCK_UFS2 65536 #define SBLOCK_PIGGY 262144 #define SBLOCKSIZE 8192 #define SBLOCKSEARCH \ { SBLOCK_UFS2, SBLOCK_UFS1, SBLOCK_FLOPPY, SBLOCK_PIGGY, -1 } /* * Addresses stored in inodes are capable of addressing fragments * of `blocks'. Filesystem blocks of at most size MAXBSIZE can * be optionally broken into 2, 4, or 8 pieces, each of which is * addressible; these pieces may be DEV_BSIZE, or some multiple of * a DEV_BSIZE unit. * * Large files consist of exclusively large data blocks. To avoid * undue wasted disk space, the last data block of a small file may be * allocated as only as many fragments of a large block as are * necessary. The filesystem format retains only a single pointer * to such a fragment, which is a piece of a single large block that * has been divided. The size of such a fragment is determinable from * information in the inode, using the ``blksize(fs, ip, lbn)'' macro. * * The filesystem records space availability at the fragment level; * to determine block availability, aligned fragments are examined. */ /* * MINBSIZE is the smallest allowable block size. * In order to insure that it is possible to create files of size * 2^32 with only two levels of indirection, MINBSIZE is set to 4096. * MINBSIZE must be big enough to hold a cylinder group block, * thus changes to (struct cg) must keep its size within MINBSIZE. * Note that super blocks are always of size SBSIZE, * and that both SBSIZE and MAXBSIZE must be >= MINBSIZE. */ #define MINBSIZE 4096 /* * The path name on which the filesystem is mounted is maintained * in fs_fsmnt. MAXMNTLEN defines the amount of space allocated in * the super block for this name. */ #define MAXMNTLEN 512 /* * There is a 128-byte region in the superblock reserved for in-core * pointers to summary information. Originally this included an array * of pointers to blocks of struct csum; now there are just three * pointers and the remaining space is padded with fs_ocsp[]. * * NOCSPTRS determines the size of this padding. One pointer (fs_csp) * is taken away to point to a contiguous array of struct csum for * all cylinder groups; a second (fs_maxcluster) points to an array * of cluster sizes that is computed as cylinder groups are inspected, * and the third points to an array that tracks the creation of new * directories. */ #define NOCSPTRS ((128 / sizeof(void *)) - 3) /* * A summary of contiguous blocks of various sizes is maintained * in each cylinder group. Normally this is set by the initial * value of fs_maxcontig. To conserve space, a maximum summary size * is set by FS_MAXCONTIG. */ #define FS_MAXCONTIG 16 /* * MINFREE gives the minimum acceptable percentage of filesystem * blocks which may be free. If the freelist drops below this level * only the superuser may continue to allocate blocks. This may * be set to 0 if no reserve of free blocks is deemed necessary, * however throughput drops by fifty percent if the filesystem * is run at between 95% and 100% full; thus the minimum default * value of fs_minfree is 5%. However, to get good clustering * performance, 10% is a better choice. hence we use 10% as our * default value. With 10% free space, fragmentation is not a * problem, so we choose to optimize for time. */ #define MINFREE 5 #define DEFAULTOPT FS_OPTTIME /* * The directory preference algorithm(dirpref) can be tuned by adjusting * the following parameters which tell the system the average file size * and the average number of files per directory. These defaults are well * selected for typical filesystems, but may need to be tuned for odd * cases like filesystems being used for sqiud caches or news spools. */ #define AVFILESIZ 16384 /* expected average file size */ #define AFPDIR 64 /* expected number of files per directory */ /* * Per cylinder group information; summarized in blocks allocated * from first cylinder group data blocks. These blocks have to be * read in from fs_csaddr (size fs_cssize) in addition to the * super block. */ struct csum { int32_t cs_ndir; /* number of directories */ int32_t cs_nbfree; /* number of free blocks */ int32_t cs_nifree; /* number of free inodes */ int32_t cs_nffree; /* number of free frags */ }; /* * Super block for an FFS filesystem. */ struct fs { int32_t fs_historic_start[2]; /* used by MirBSD to store entropy */ #define fs_firstfield fs_historic_start[0] /* filesystem linked list, */ #define fs_unused_1 fs_historic_start[1] /* used for incore superblk */ int32_t fs_sblkno; /* addr of super-block in filesys */ int32_t fs_cblkno; /* offset of cyl-block in filesys */ int32_t fs_iblkno; /* offset of inode-blocks in filesys */ int32_t fs_dblkno; /* offset of first data after cg */ int32_t fs_cgoffset; /* cylinder group offset in cylinder */ int32_t fs_cgmask; /* used to calc mod fs_ntrak */ int32_t fs_time; /* last time written XXX was time_t */ int32_t fs_size; /* number of blocks in fs */ int32_t fs_dsize; /* number of data blocks in fs */ int32_t fs_ncg; /* number of cylinder groups */ int32_t fs_bsize; /* size of basic blocks in fs */ int32_t fs_fsize; /* size of frag blocks in fs */ int32_t fs_frag; /* number of frags in a block in fs */ /* these are configuration parameters */ int32_t fs_minfree; /* minimum percentage of free blocks */ int32_t fs_rotdelay; /* num of ms for optimal next block */ int32_t fs_rps; /* disk revolutions per second */ /* these fields can be computed from the others */ int32_t fs_bmask; /* ``blkoff'' calc of blk offsets */ int32_t fs_fmask; /* ``fragoff'' calc of frag offsets */ int32_t fs_bshift; /* ``lblkno'' calc of logical blkno */ int32_t fs_fshift; /* ``numfrags'' calc number of frags */ /* these are configuration parameters */ int32_t fs_maxcontig; /* max number of contiguous blks */ int32_t fs_maxbpg; /* max number of blks per cyl group */ /* these fields can be computed from the others */ int32_t fs_fragshift; /* block to frag shift */ int32_t fs_fsbtodb; /* fsbtodb and dbtofsb shift constant */ int32_t fs_sbsize; /* actual size of super block */ int32_t fs_csmask; /* csum block offset (now unused) */ int32_t fs_csshift; /* csum block number (now unused) */ int32_t fs_nindir; /* value of NINDIR */ int32_t fs_inopb; /* value of INOPB */ int32_t fs_nspf; /* value of NSPF */ /* yet another configuration parameter */ int32_t fs_optim; /* optimization preference, see below */ /* these fields are derived from the hardware */ int32_t fs_npsect; /* # sectors/track including spares */ int32_t fs_interleave; /* hardware sector interleave */ int32_t fs_trackskew; /* sector 0 skew, per track */ /* fs_id takes the space of the unused fs_headswitch and fs_trkseek fields */ int32_t fs_id[2]; /* unique filesystem id */ /* sizes determined by number of cylinder groups and their sizes */ int32_t fs_csaddr; /* blk addr of cyl grp summary area */ int32_t fs_cssize; /* size of cyl grp summary area */ int32_t fs_cgsize; /* cylinder group size */ /* these fields are derived from the hardware */ int32_t fs_ntrak; /* tracks per cylinder */ int32_t fs_nsect; /* sectors per track */ int32_t fs_spc; /* sectors per cylinder */ /* this comes from the disk driver partitioning */ int32_t fs_ncyl; /* cylinders in filesystem */ /* these fields can be computed from the others */ int32_t fs_cpg; /* cylinders per group */ int32_t fs_ipg; /* inodes per group */ int32_t fs_fpg; /* blocks per group * fs_frag */ /* this data must be re-computed after crashes */ struct csum fs_cstotal; /* cylinder summary information */ /* these fields are cleared at mount time */ int8_t fs_fmod; /* super block modified flag */ int8_t fs_clean; /* filesystem is clean flag */ int8_t fs_ronly; /* mounted read-only flag */ int8_t fs_flags; /* see FS_ below */ u_char fs_fsmnt[MAXMNTLEN]; /* name mounted on */ /* these fields retain the current block allocation info */ int32_t fs_cgrotor; /* last cg searched */ void *fs_ocsp[NOCSPTRS]; /* padding; was list of fs_cs buffers */ u_int8_t *fs_contigdirs; /* # of contiguously allocated dirs */ struct csum *fs_csp; /* cg summary info buffer for fs_cs */ int32_t *fs_maxcluster; /* max cluster in each cyl group */ int32_t fs_cpc; /* cyl per cycle in postbl */ int16_t fs_opostbl[16][8]; /* old rotation block list head */ int32_t fs_snapinum[20]; /* reserved for snapshot inode nums */ int32_t fs_avgfilesize; /* expected average file size */ int32_t fs_avgfpdir; /* expected # of files per directory */ int32_t fs_sparecon[27]; /* reserved for future constants */ int32_t fs_fscktime; /* last time fsck(8)ed XXX was time_t */ int32_t fs_contigsumsize; /* size of cluster summary array */ int32_t fs_maxsymlinklen; /* max length of an internal symlink */ int32_t fs_inodefmt; /* format of on-disk inodes */ u_int64_t fs_maxfilesize; /* maximum representable file size */ int64_t fs_qbmask; /* ~fs_bmask - for use with quad size */ int64_t fs_qfmask; /* ~fs_fmask - for use with quad size */ int32_t fs_state; /* validate fs_clean field */ int32_t fs_postblformat; /* format of positional layout tables */ int32_t fs_nrpos; /* number of rotational positions */ int32_t fs_postbloff; /* (u_int16) rotation block list head */ int32_t fs_rotbloff; /* (u_int8) blocks for each rotation */ int32_t fs_magic; /* magic number */ u_int8_t fs_space[1]; /* list of blocks for each rotation */ /* actually longer */ }; #define fs_opostbl_start fs_opostbl[0][0] /* * Filesystem identification */ #define FS_MAGIC 0x011954 /* the fast filesystem magic number */ #define FS_UFS1_MAGIC 0x011954 /* the fast filesystem magic number */ #define FS_UFS2_MAGIC 0x19540119 /* UFS fast filesystem magic number */ #define FS_OKAY 0x7c269d38 /* superblock checksum */ #define FS_42INODEFMT -1 /* 4.2BSD inode format */ #define FS_44INODEFMT 2 /* 4.4BSD inode format */ /* * Filesystem clean flags */ #define FS_ISCLEAN 0x01 #define FS_WASCLEAN 0x02 /* * Preference for optimization. */ #define FS_OPTTIME 0 /* minimize allocation time */ #define FS_OPTSPACE 1 /* minimize disk fragmentation */ /* * Filesystem flags. */ #define FS_UNCLEAN 0x01 /* filesystem not clean at mount */ #define FS_DOSOFTDEP 0x02 /* filesystem using soft dependencies */ /* * Rotational layout table format types */ #define FS_42POSTBLFMT -1 /* 4.2BSD rotational table format */ #define FS_DYNAMICPOSTBLFMT 1 /* dynamic rotational table format */ /* * Macros for access to superblock array structures */ #define fs_postbl(fs, cylno) \ (((fs)->fs_postblformat == FS_42POSTBLFMT) \ ? ((fs)->fs_opostbl[cylno]) \ : ((int16_t *)((u_int8_t *)(fs) + \ (fs)->fs_postbloff) + (cylno) * (fs)->fs_nrpos)) #define fs_rotbl(fs) \ (((fs)->fs_postblformat == FS_42POSTBLFMT) \ ? ((fs)->fs_space) \ : ((u_int8_t *)((u_int8_t *)(fs) + (fs)->fs_rotbloff))) /* * The size of a cylinder group is calculated by CGSIZE. The maximum size * is limited by the fact that cylinder groups are at most one block. * Its size is derived from the size of the maps maintained in the * cylinder group and the (struct cg) size. */ #define CGSIZE(fs) \ /* base cg */ (sizeof(struct cg) + sizeof(int32_t) + \ /* blktot size */ (fs)->fs_cpg * sizeof(int32_t) + \ /* blks size */ (fs)->fs_cpg * (fs)->fs_nrpos * sizeof(int16_t) + \ /* inode map */ howmany((fs)->fs_ipg, NBBY) + \ /* block map */ howmany((fs)->fs_cpg * (fs)->fs_spc / NSPF(fs), NBBY) +\ /* if present */ ((fs)->fs_contigsumsize <= 0 ? 0 : \ /* cluster sum */ (fs)->fs_contigsumsize * sizeof(int32_t) + \ /* cluster map */ howmany((fs)->fs_cpg * (fs)->fs_spc / NSPB(fs), NBBY))) /* * Convert cylinder group to base address of its global summary info. */ #define fs_cs(fs, indx) fs_csp[indx] /* * Cylinder group block for a filesystem. */ #define CG_MAGIC 0x090255 struct cg { int32_t cg_firstfield; /* historic cyl groups linked list */ int32_t cg_magic; /* magic number */ int32_t cg_time; /* time last written XXX was time_t */ int32_t cg_cgx; /* we are the cgx'th cylinder group */ int16_t cg_ncyl; /* number of cyl's this cg */ int16_t cg_niblk; /* number of inode blocks this cg */ int32_t cg_ndblk; /* number of data blocks this cg */ struct csum cg_cs; /* cylinder summary information */ int32_t cg_rotor; /* position of last used block */ int32_t cg_frotor; /* position of last used frag */ int32_t cg_irotor; /* position of last used inode */ int32_t cg_frsum[MAXFRAG]; /* counts of available frags */ int32_t cg_btotoff; /* (int32) block totals per cylinder */ int32_t cg_boff; /* (u_int16) free block positions */ int32_t cg_iusedoff; /* (u_int8) used inode map */ int32_t cg_freeoff; /* (u_int8) free block map */ int32_t cg_nextfreeoff; /* (u_int8) next available space */ int32_t cg_clustersumoff; /* (u_int32) counts of avail clusters */ int32_t cg_clusteroff; /* (u_int8) free cluster map */ int32_t cg_nclusterblks; /* number of clusters this cg */ int32_t cg_ffs2_niblk; /* number of inode blocks this cg */ int32_t cg_initediblk; /* last initialized inode */ int32_t cg_sparecon32[3]; /* reserved for future use */ int64_t cg_ffs2_time; /* time last written */ int64_t cg_sparecon64[3]; /* reserved for future use */ u_int8_t cg_space[1]; /* space for cylinder group maps */ /* actually longer */ }; /* * Macros for access to cylinder group array structures */ #define cg_blktot(cgp) \ (((cgp)->cg_magic != CG_MAGIC) \ ? (((struct ocg *)(cgp))->cg_btot) \ : ((int32_t *)((u_int8_t *)(cgp) + (cgp)->cg_btotoff))) #define cg_blks(fs, cgp, cylno) \ (((cgp)->cg_magic != CG_MAGIC) \ ? (((struct ocg *)(cgp))->cg_b[cylno]) \ : ((int16_t *)((u_int8_t *)(cgp) + \ (cgp)->cg_boff) + (cylno) * (fs)->fs_nrpos)) #define cg_inosused(cgp) \ (((cgp)->cg_magic != CG_MAGIC) \ ? (((struct ocg *)(cgp))->cg_iused) \ : ((u_int8_t *)((u_int8_t *)(cgp) + (cgp)->cg_iusedoff))) #define cg_blksfree(cgp) \ (((cgp)->cg_magic != CG_MAGIC) \ ? (((struct ocg *)(cgp))->cg_free) \ : ((u_int8_t *)((u_int8_t *)(cgp) + (cgp)->cg_freeoff))) #define cg_chkmagic(cgp) \ ((cgp)->cg_magic == CG_MAGIC || ((struct ocg *)(cgp))->cg_magic == CG_MAGIC) #define cg_clustersfree(cgp) \ ((u_int8_t *)((u_int8_t *)(cgp) + (cgp)->cg_clusteroff)) #define cg_clustersum(cgp) \ ((int32_t *)((u_int8_t *)(cgp) + (cgp)->cg_clustersumoff)) /* * The following structure is defined * for compatibility with old filesystems. */ struct ocg { int32_t cg_firstfield; /* historic linked list of cyl groups */ int32_t cg_unused_1; /* used for incore cyl groups */ int32_t cg_time; /* time last written XXX was time_t */ int32_t cg_cgx; /* we are the cgx'th cylinder group */ int16_t cg_ncyl; /* number of cyl's this cg */ int16_t cg_niblk; /* number of inode blocks this cg */ int32_t cg_ndblk; /* number of data blocks this cg */ struct csum cg_cs; /* cylinder summary information */ int32_t cg_rotor; /* position of last used block */ int32_t cg_frotor; /* position of last used frag */ int32_t cg_irotor; /* position of last used inode */ int32_t cg_frsum[8]; /* counts of available frags */ int32_t cg_btot[32]; /* block totals per cylinder */ int16_t cg_b[32][8]; /* positions of free blocks */ u_int8_t cg_iused[256]; /* used inode map */ int32_t cg_magic; /* magic number */ u_int8_t cg_free[1]; /* free block map */ /* actually longer */ }; /* * Turn filesystem block numbers into disk block addresses. * This maps filesystem blocks to device size blocks. */ #define fsbtodb(fs, b) ((b) << (fs)->fs_fsbtodb) #define dbtofsb(fs, b) ((b) >> (fs)->fs_fsbtodb) /* * Cylinder group macros to locate things in cylinder groups. * They calc filesystem addresses of cylinder group data structures. */ #define cgbase(fs, c) ((daddr_t)((fs)->fs_fpg * (c))) #define cgdmin(fs, c) (cgstart(fs, c) + (fs)->fs_dblkno) /* 1st data */ #define cgimin(fs, c) (cgstart(fs, c) + (fs)->fs_iblkno) /* inode blk */ #define cgsblock(fs, c) (cgstart(fs, c) + (fs)->fs_sblkno) /* super blk */ #define cgtod(fs, c) (cgstart(fs, c) + (fs)->fs_cblkno) /* cg block */ #define cgstart(fs, c) \ (cgbase(fs, c) + (fs)->fs_cgoffset * ((c) & ~((fs)->fs_cgmask))) /* * Macros for handling inode numbers: * inode number to filesystem block offset. * inode number to cylinder group number. * inode number to filesystem block address. */ #define ino_to_cg(fs, x) ((x) / (fs)->fs_ipg) #define ino_to_fsba(fs, x) \ ((daddr_t)(cgimin(fs, ino_to_cg(fs, x)) + \ (blkstofrags((fs), (((x) % (fs)->fs_ipg) / INOPB(fs)))))) #define ino_to_fsbo(fs, x) ((x) % INOPB(fs)) /* * Give cylinder group number for a filesystem block. * Give cylinder group block number for a filesystem block. */ #define dtog(fs, d) ((d) / (fs)->fs_fpg) #define dtogd(fs, d) ((d) % (fs)->fs_fpg) /* * Extract the bits for a block from a map. * Compute the cylinder and rotational position of a cyl block addr. */ #define blkmap(fs, map, loc) \ (((map)[(loc) / NBBY] >> ((loc) % NBBY)) & (0xff >> (NBBY - (fs)->fs_frag))) #define cbtocylno(fs, bno) \ ((bno) * NSPF(fs) / (fs)->fs_spc) #define cbtorpos(fs, bno) \ (((bno) * NSPF(fs) % (fs)->fs_spc / (fs)->fs_nsect * (fs)->fs_trackskew + \ (bno) * NSPF(fs) % (fs)->fs_spc % (fs)->fs_nsect * (fs)->fs_interleave) % \ (fs)->fs_nsect * (fs)->fs_nrpos / (fs)->fs_npsect) /* * The following macros optimize certain frequently calculated * quantities by using shifts and masks in place of divisions * modulos and multiplications. */ #define blkoff(fs, loc) /* calculates (loc % fs->fs_bsize) */ \ ((loc) & (fs)->fs_qbmask) #define fragoff(fs, loc) /* calculates (loc % fs->fs_fsize) */ \ ((loc) & (fs)->fs_qfmask) #define lblktosize(fs, blk) /* calculates ((off_t)blk * fs->fs_bsize) */ \ ((off_t)(blk) << (fs)->fs_bshift) #define lblkno(fs, loc) /* calculates (loc / fs->fs_bsize) */ \ ((loc) >> (fs)->fs_bshift) #define numfrags(fs, loc) /* calculates (loc / fs->fs_fsize) */ \ ((loc) >> (fs)->fs_fshift) #define blkroundup(fs, size) /* calculates roundup(size, fs->fs_bsize) */ \ (((size) + (fs)->fs_qbmask) & (fs)->fs_bmask) #define fragroundup(fs, size) /* calculates roundup(size, fs->fs_fsize) */ \ (((size) + (fs)->fs_qfmask) & (fs)->fs_fmask) #define fragstoblks(fs, frags) /* calculates (frags / fs->fs_frag) */ \ ((frags) >> (fs)->fs_fragshift) #define blkstofrags(fs, blks) /* calculates (blks * fs->fs_frag) */ \ ((blks) << (fs)->fs_fragshift) #define fragnum(fs, fsb) /* calculates (fsb % fs->fs_frag) */ \ ((fsb) & ((fs)->fs_frag - 1)) #define blknum(fs, fsb) /* calculates rounddown(fsb, fs->fs_frag) */ \ ((fsb) &~ ((fs)->fs_frag - 1)) /* * Determine the number of available frags given a * percentage to hold in reserve. */ #define freespace(fs, percentreserved) \ (blkstofrags((fs), (fs)->fs_cstotal.cs_nbfree) + \ (fs)->fs_cstotal.cs_nffree - ((fs)->fs_dsize * (percentreserved) / 100)) /* * Determining the size of a file block in the filesystem. */ #define blksize(fs, ip, lbn) \ (((lbn) >= NDADDR || (ip)->i_ffs_size >= ((lbn) + 1) << (fs)->fs_bshift) \ ? (fs)->fs_bsize \ : (fragroundup(fs, blkoff(fs, (ip)->i_ffs_size)))) #define dblksize(fs, dip, lbn) \ (((lbn) >= NDADDR || (dip)->di_size >= ((lbn) + 1) << (fs)->fs_bshift) \ ? (fs)->fs_bsize \ : (fragroundup(fs, blkoff(fs, (dip)->di_size)))) #define sblksize(fs, size, lbn) \ (((lbn) >= NDADDR || (size) >= ((lbn) + 1) << (fs)->fs_bshift) \ ? (fs)->fs_bsize \ : (fragroundup(fs, blkoff(fs, (size))))) /* * Number of disk sectors per block/fragment; assumes DEV_BSIZE byte * sector size. */ #define NSPB(fs) ((fs)->fs_nspf << (fs)->fs_fragshift) #define NSPF(fs) ((fs)->fs_nspf) /* * Number of inodes in a secondary storage block/fragment. */ #define INOPB(fs) ((fs)->fs_inopb) #define INOPF(fs) ((fs)->fs_inopb >> (fs)->fs_fragshift) /* * Number of indirects in a filesystem block. */ #define NINDIR(fs) ((fs)->fs_nindir) extern const int inside[], around[]; extern const u_char *fragtbl[]; #endif /* notdef UFS_FFS_FS_H */ makefs/src/sys/ufs/ffs/softdep.h010064400000000000000000000733011150507305000140500ustar00/* $OpenBSD: softdep.h,v 1.10 2005/07/20 16:30:35 pedro Exp $ */ /* * Copyright 1998, 2000 Marshall Kirk McKusick. All Rights Reserved. * * The soft updates code is derived from the appendix of a University * of Michigan technical report (Gregory R. Ganger and Yale N. Patt, * "Soft Updates: A Solution to the Metadata Update Problem in File * Systems", CSE-TR-254-95, August 1995). * * Further information about soft updates can be obtained from: * * Marshall Kirk McKusick http://www.mckusick.com/softdep/ * 1614 Oxford Street mckusick@mckusick.com * Berkeley, CA 94709-1608 +1-510-843-9542 * USA * * 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 MARSHALL KIRK MCKUSICK ``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 MARSHALL KIRK MCKUSICK 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. * * @(#)softdep.h 9.7 (McKusick) 6/21/00 * $FreeBSD: src/sys/ufs/ffs/softdep.h,v 1.10 2000/06/22 00:29:53 mckusick Exp $ */ #include /* * Allocation dependencies are handled with undo/redo on the in-memory * copy of the data. A particular data dependency is eliminated when * it is ALLCOMPLETE: that is ATTACHED, DEPCOMPLETE, and COMPLETE. * * ATTACHED means that the data is not currently being written to * disk. UNDONE means that the data has been rolled back to a safe * state for writing to the disk. When the I/O completes, the data is * restored to its current form and the state reverts to ATTACHED. * The data must be locked throughout the rollback, I/O, and roll * forward so that the rolled back information is never visible to * user processes. The COMPLETE flag indicates that the item has been * written. For example, a dependency that requires that an inode be * written will be marked COMPLETE after the inode has been written * to disk. The DEPCOMPLETE flag indicates the completion of any other * dependencies such as the writing of a cylinder group map has been * completed. A dependency structure may be freed only when both it * and its dependencies have completed and any rollbacks that are in * progress have finished as indicated by the set of ALLCOMPLETE flags * all being set. The two MKDIR flags indicate additional dependencies * that must be done when creating a new directory. MKDIR_BODY is * cleared when the directory data block containing the "." and ".." * entries has been written. MKDIR_PARENT is cleared when the parent * inode with the increased link count for ".." has been written. When * both MKDIR flags have been cleared, the DEPCOMPLETE flag is set to * indicate that the directory dependencies have been completed. The * writing of the directory inode itself sets the COMPLETE flag which * then allows the directory entry for the new directory to be written * to disk. The RMDIR flag marks a dirrem structure as representing * the removal of a directory rather than a file. When the removal * dependencies are completed, additional work needs to be done * (truncation of the "." and ".." entries, an additional decrement * of the associated inode, and a decrement of the parent inode). The * DIRCHG flag marks a diradd structure as representing the changing * of an existing entry rather than the addition of a new one. When * the update is complete the dirrem associated with the inode for * the old name must be added to the worklist to do the necessary * reference count decrement. The GOINGAWAY flag indicates that the * data structure is frozen from further change until its dependencies * have been completed and its resources freed after which it will be * discarded. The IOSTARTED flag prevents multiple calls to the I/O * start routine from doing multiple rollbacks. The SPACECOUNTED flag * says that the files space has been accounted to the pending free * space count. The NEWBLOCK flag marks pagedep structures that have * just been allocated, so must be claimed by the inode before all * dependencies are complete. The ONWORKLIST flag shows whether the * structure is currently linked onto a worklist * */ #define ATTACHED 0x0001 #define UNDONE 0x0002 #define COMPLETE 0x0004 #define DEPCOMPLETE 0x0008 #define MKDIR_PARENT 0x0010 /* diradd & mkdir only */ #define MKDIR_BODY 0x0020 /* diradd & mkdir only */ #define RMDIR 0x0040 /* dirrem only */ #define DIRCHG 0x0080 /* diradd & dirrem only */ #define GOINGAWAY 0x0100 /* indirdep only */ #define IOSTARTED 0x0200 /* inodedep & pagedep only */ #define SPACECOUNTED 0x0400 /* inodedep only */ #define NEWBLOCK 0x0800 /* pagedep only */ #define ONWORKLIST 0x8000 #define ALLCOMPLETE (ATTACHED | COMPLETE | DEPCOMPLETE) /* * The workitem queue. * * It is sometimes useful and/or necessary to clean up certain dependencies * in the background rather than during execution of an application process * or interrupt service routine. To realize this, we append dependency * structures corresponding to such tasks to a "workitem" queue. In a soft * updates implementation, most pending workitems should not wait for more * than a couple of seconds, so the filesystem syncer process awakens once * per second to process the items on the queue. */ /* LIST_HEAD(workhead, worklist); -- declared in buf.h */ /* * Each request can be linked onto a work queue through its worklist structure. * To avoid the need for a pointer to the structure itself, this structure * MUST be declared FIRST in each type in which it appears! If more than one * worklist is needed in the structure, then a wk_data field must be added * and the macros below changed to use it. */ struct worklist { LIST_ENTRY(worklist) wk_list; /* list of work requests */ unsigned short wk_type; /* type of request */ unsigned short wk_state; /* state flags */ }; #define WK_DATA(wk) ((void *)(wk)) #define WK_PAGEDEP(wk) ((struct pagedep *)(wk)) #define WK_INODEDEP(wk) ((struct inodedep *)(wk)) #define WK_NEWBLK(wk) ((struct newblk *)(wk)) #define WK_BMSAFEMAP(wk) ((struct bmsafemap *)(wk)) #define WK_ALLOCDIRECT(wk) ((struct allocdirect *)(wk)) #define WK_INDIRDEP(wk) ((struct indirdep *)(wk)) #define WK_ALLOCINDIR(wk) ((struct allocindir *)(wk)) #define WK_FREEFRAG(wk) ((struct freefrag *)(wk)) #define WK_FREEBLKS(wk) ((struct freeblks *)(wk)) #define WK_FREEFILE(wk) ((struct freefile *)(wk)) #define WK_DIRADD(wk) ((struct diradd *)(wk)) #define WK_MKDIR(wk) ((struct mkdir *)(wk)) #define WK_DIRREM(wk) ((struct dirrem *)(wk)) #define WK_NEWDIRBLK(wk) ((struct newdirblk *)(wk)) /* * Various types of lists */ LIST_HEAD(dirremhd, dirrem); LIST_HEAD(diraddhd, diradd); LIST_HEAD(newblkhd, newblk); LIST_HEAD(inodedephd, inodedep); LIST_HEAD(allocindirhd, allocindir); LIST_HEAD(allocdirecthd, allocdirect); TAILQ_HEAD(allocdirectlst, allocdirect); /* * The "pagedep" structure tracks the various dependencies related to * a particular directory page. If a directory page has any dependencies, * it will have a pagedep linked to its associated buffer. The * pd_dirremhd list holds the list of dirrem requests which decrement * inode reference counts. These requests are processed after the * directory page with the corresponding zero'ed entries has been * written. The pd_diraddhd list maintains the list of diradd requests * which cannot be committed until their corresponding inode has been * written to disk. Because a directory may have many new entries * being created, several lists are maintained hashed on bits of the * offset of the entry into the directory page to keep the lists from * getting too long. Once a new directory entry has been cleared to * be written, it is moved to the pd_pendinghd list. After the new * entry has been written to disk it is removed from the pd_pendinghd * list, any removed operations are done, and the dependency structure * is freed. */ #define DAHASHSZ 6 #define DIRADDHASH(offset) (((offset) >> 2) % DAHASHSZ) struct pagedep { struct worklist pd_list; /* page buffer */ # define pd_state pd_list.wk_state /* check for multiple I/O starts */ LIST_ENTRY(pagedep) pd_hash; /* hashed lookup */ struct mount *pd_mnt; /* associated mount point */ ino_t pd_ino; /* associated file */ ufs_lbn_t pd_lbn; /* block within file */ struct dirremhd pd_dirremhd; /* dirrem's waiting for page */ struct diraddhd pd_diraddhd[DAHASHSZ]; /* diradd dir entry updates */ struct diraddhd pd_pendinghd; /* directory entries awaiting write */ }; /* * The "inodedep" structure tracks the set of dependencies associated * with an inode. One task that it must manage is delayed operations * (i.e., work requests that must be held until the inodedep's associated * inode has been written to disk). Getting an inode from its incore * state to the disk requires two steps to be taken by the filesystem * in this order: first the inode must be copied to its disk buffer by * the VOP_UPDATE operation; second the inode's buffer must be written * to disk. To ensure that both operations have happened in the required * order, the inodedep maintains two lists. Delayed operations are * placed on the id_inowait list. When the VOP_UPDATE is done, all * operations on the id_inowait list are moved to the id_bufwait list. * When the buffer is written, the items on the id_bufwait list can be * safely moved to the work queue to be processed. A second task of the * inodedep structure is to track the status of block allocation within * the inode. Each block that is allocated is represented by an * "allocdirect" structure (see below). It is linked onto the id_newinoupdt * list until both its contents and its allocation in the cylinder * group map have been written to disk. Once these dependencies have been * satisfied, it is removed from the id_newinoupdt list and any followup * actions such as releasing the previous block or fragment are placed * on the id_inowait list. When an inode is updated (a VOP_UPDATE is * done), the "inodedep" structure is linked onto the buffer through * its worklist. Thus, it will be notified when the buffer is about * to be written and when it is done. At the update time, all the * elements on the id_newinoupdt list are moved to the id_inoupdt list * since those changes are now relevant to the copy of the inode in the * buffer. Also at update time, the tasks on the id_inowait list are * moved to the id_bufwait list so that they will be executed when * the updated inode has been written to disk. When the buffer containing * the inode is written to disk, any updates listed on the id_inoupdt * list are rolled back as they are not yet safe. Following the write, * the changes are once again rolled forward and any actions on the * id_bufwait list are processed (since those actions are now safe). * The entries on the id_inoupdt and id_newinoupdt lists must be kept * sorted by logical block number to speed the calculation of the size * of the rolled back inode (see explanation in initiate_write_inodeblock). * When a directory entry is created, it is represented by a diradd. * The diradd is added to the id_inowait list as it cannot be safely * written to disk until the inode that it represents is on disk. After * the inode is written, the id_bufwait list is processed and the diradd * entries are moved to the id_pendinghd list where they remain until * the directory block containing the name has been written to disk. * The purpose of keeping the entries on the id_pendinghd list is so that * the softdep_fsync function can find and push the inode's directory * name(s) as part of the fsync operation for that file. */ struct inodedep { struct worklist id_list; /* buffer holding inode block */ # define id_state id_list.wk_state /* inode dependency state */ LIST_ENTRY(inodedep) id_hash; /* hashed lookup */ struct fs *id_fs; /* associated filesystem */ ino_t id_ino; /* dependent inode */ nlink_t id_nlinkdelta; /* saved effective link count */ struct ufs1_dinode *id_savedino; /* saved dinode contents */ LIST_ENTRY(inodedep) id_deps; /* bmsafemap's list of inodedep's */ struct buf *id_buf; /* related bmsafemap (if pending) */ off_t id_savedsize; /* file size saved during rollback */ struct workhead id_pendinghd; /* entries awaiting directory write */ struct workhead id_bufwait; /* operations after inode written */ struct workhead id_inowait; /* operations waiting inode update */ struct allocdirectlst id_inoupdt; /* updates before inode written */ struct allocdirectlst id_newinoupdt; /* updates when inode written */ }; /* * A "newblk" structure is attached to a bmsafemap structure when a block * or fragment is allocated from a cylinder group. Its state is set to * DEPCOMPLETE when its cylinder group map is written. It is consumed by * an associated allocdirect or allocindir allocation which will attach * themselves to the bmsafemap structure if the newblk's DEPCOMPLETE flag * is not set (i.e., its cylinder group map has not been written). */ struct newblk { LIST_ENTRY(newblk) nb_hash; /* hashed lookup */ struct fs *nb_fs; /* associated filesystem */ daddr_t nb_newblkno; /* allocated block number */ int nb_state; /* state of bitmap dependency */ LIST_ENTRY(newblk) nb_deps; /* bmsafemap's list of newblk's */ struct bmsafemap *nb_bmsafemap; /* associated bmsafemap */ }; /* * A "bmsafemap" structure maintains a list of dependency structures * that depend on the update of a particular cylinder group map. * It has lists for newblks, allocdirects, allocindirs, and inodedeps. * It is attached to the buffer of a cylinder group block when any of * these things are allocated from the cylinder group. It is freed * after the cylinder group map is written and the state of its * dependencies are updated with DEPCOMPLETE to indicate that it has * been processed. */ struct bmsafemap { struct worklist sm_list; /* cylgrp buffer */ struct buf *sm_buf; /* associated buffer */ struct allocdirecthd sm_allocdirecthd; /* allocdirect deps */ struct allocindirhd sm_allocindirhd; /* allocindir deps */ struct inodedephd sm_inodedephd; /* inodedep deps */ struct newblkhd sm_newblkhd; /* newblk deps */ }; /* * An "allocdirect" structure is attached to an "inodedep" when a new block * or fragment is allocated and pointed to by the inode described by * "inodedep". The worklist is linked to the buffer that holds the block. * When the block is first allocated, it is linked to the bmsafemap * structure associated with the buffer holding the cylinder group map * from which it was allocated. When the cylinder group map is written * to disk, ad_state has the DEPCOMPLETE flag set. When the block itself * is written, the COMPLETE flag is set. Once both the cylinder group map * and the data itself have been written, it is safe to write the inode * that claims the block. If there was a previous fragment that had been * allocated before the file was increased in size, the old fragment may * be freed once the inode claiming the new block is written to disk. * This ad_fragfree request is attached to the id_inowait list of the * associated inodedep (pointed to by ad_inodedep) for processing after * the inode is written. When a block is allocated to a directory, an * fsync of a file whose name is within that block must ensure not only * that the block containing the file name has been written, but also * that the on-disk inode references that block. When a new directory * block is created, we allocate a newdirblk structure which is linked * to the associated allocdirect (on its ad_newdirblk list). When the * allocdirect has been satisfied, the newdirblk structure is moved to * the inodedep id_bufwait list of its directory to await the inode * being written. When the inode is written, the directory entries are * fully committed and can be deleted from their pagedep->id_pendinghd * and inodedep->id_pendinghd lists. */ struct allocdirect { struct worklist ad_list; /* buffer holding block */ # define ad_state ad_list.wk_state /* block pointer state */ TAILQ_ENTRY(allocdirect) ad_next; /* inodedep's list of allocdirect's */ ufs_lbn_t ad_lbn; /* block within file */ daddr_t ad_newblkno; /* new value of block pointer */ daddr_t ad_oldblkno; /* old value of block pointer */ long ad_newsize; /* size of new block */ long ad_oldsize; /* size of old block */ LIST_ENTRY(allocdirect) ad_deps; /* bmsafemap's list of allocdirect's */ struct buf *ad_buf; /* cylgrp buffer (if pending) */ struct inodedep *ad_inodedep; /* associated inodedep */ struct freefrag *ad_freefrag; /* fragment to be freed (if any) */ struct workhead ad_newdirblk; /* dir block to notify when written */ }; /* * A single "indirdep" structure manages all allocation dependencies for * pointers in an indirect block. The up-to-date state of the indirect * block is stored in ir_savedata. The set of pointers that may be safely * written to the disk is stored in ir_safecopy. The state field is used * only to track whether the buffer is currently being written (in which * case it is not safe to update ir_safecopy). Ir_deplisthd contains the * list of allocindir structures, one for each block that needs to be * written to disk. Once the block and its bitmap allocation have been * written the safecopy can be updated to reflect the allocation and the * allocindir structure freed. If ir_state indicates that an I/O on the * indirect block is in progress when ir_safecopy is to be updated, the * update is deferred by placing the allocindir on the ir_donehd list. * When the I/O on the indirect block completes, the entries on the * ir_donehd list are processed by updating their corresponding ir_safecopy * pointers and then freeing the allocindir structure. */ struct indirdep { struct worklist ir_list; /* buffer holding indirect block */ # define ir_state ir_list.wk_state /* indirect block pointer state */ caddr_t ir_saveddata; /* buffer cache contents */ struct buf *ir_savebp; /* buffer holding safe copy */ struct allocindirhd ir_donehd; /* done waiting to update safecopy */ struct allocindirhd ir_deplisthd; /* allocindir deps for this block */ }; /* * An "allocindir" structure is attached to an "indirdep" when a new block * is allocated and pointed to by the indirect block described by the * "indirdep". The worklist is linked to the buffer that holds the new block. * When the block is first allocated, it is linked to the bmsafemap * structure associated with the buffer holding the cylinder group map * from which it was allocated. When the cylinder group map is written * to disk, ai_state has the DEPCOMPLETE flag set. When the block itself * is written, the COMPLETE flag is set. Once both the cylinder group map * and the data itself have been written, it is safe to write the entry in * the indirect block that claims the block; the "allocindir" dependency * can then be freed as it is no longer applicable. */ struct allocindir { struct worklist ai_list; /* buffer holding indirect block */ # define ai_state ai_list.wk_state /* indirect block pointer state */ LIST_ENTRY(allocindir) ai_next; /* indirdep's list of allocindir's */ int ai_offset; /* pointer offset in indirect block */ daddr_t ai_newblkno; /* new block pointer value */ daddr_t ai_oldblkno; /* old block pointer value */ struct freefrag *ai_freefrag; /* block to be freed when complete */ struct indirdep *ai_indirdep; /* address of associated indirdep */ LIST_ENTRY(allocindir) ai_deps; /* bmsafemap's list of allocindir's */ struct buf *ai_buf; /* cylgrp buffer (if pending) */ }; /* * A "freefrag" structure is attached to an "inodedep" when a previously * allocated fragment is replaced with a larger fragment, rather than extended. * The "freefrag" structure is constructed and attached when the replacement * block is first allocated. It is processed after the inode claiming the * bigger block that replaces it has been written to disk. Note that the * ff_state field is is used to store the uid, so may lose data. However, * the uid is used only in printing an error message, so is not critical. * Keeping it in a short keeps the data structure down to 32 bytes. */ struct freefrag { struct worklist ff_list; /* id_inowait or delayed worklist */ # define ff_state ff_list.wk_state /* owning user; should be uid_t */ struct vnode *ff_devvp; /* filesystem device vnode */ struct mount *ff_mnt; /* associated mount point */ daddr_t ff_blkno; /* fragment physical block number */ long ff_fragsize; /* size of fragment being deleted */ ino_t ff_inum; /* owning inode number */ }; /* * A "freeblks" structure is attached to an "inodedep" when the * corresponding file's length is reduced to zero. It records all * the information needed to free the blocks of a file after its * zero'ed inode has been written to disk. */ struct freeblks { struct worklist fb_list; /* id_inowait or delayed worklist */ # define fb_state fb_list.wk_state /* inode and dirty block state */ ino_t fb_previousinum; /* inode of previous owner of blocks */ struct vnode *fb_devvp; /* filesystem device vnode */ struct mount *fb_mnt; /* associated mount point */ off_t fb_oldsize; /* previous file size */ off_t fb_newsize; /* new file size */ int fb_chkcnt; /* used to check cnt of blks released */ uid_t fb_uid; /* uid of previous owner of blocks */ daddr_t fb_dblks[NDADDR]; /* direct blk ptrs to deallocate */ daddr_t fb_iblks[NIADDR]; /* indirect blk ptrs to deallocate */ }; /* * A "freefile" structure is attached to an inode when its * link count is reduced to zero. It marks the inode as free in * the cylinder group map after the zero'ed inode has been written * to disk and any associated blocks and fragments have been freed. */ struct freefile { struct worklist fx_list; /* id_inowait or delayed worklist */ mode_t fx_mode; /* mode of inode */ ino_t fx_oldinum; /* inum of the unlinked file */ struct vnode *fx_devvp; /* filesystem device vnode */ struct mount *fx_mnt; /* associated mount point */ }; /* * A "diradd" structure is linked to an "inodedep" id_inowait list when a * new directory entry is allocated that references the inode described * by "inodedep". When the inode itself is written (either the initial * allocation for new inodes or with the increased link count for * existing inodes), the COMPLETE flag is set in da_state. If the entry * is for a newly allocated inode, the "inodedep" structure is associated * with a bmsafemap which prevents the inode from being written to disk * until the cylinder group has been updated. Thus the da_state COMPLETE * flag cannot be set until the inode bitmap dependency has been removed. * When creating a new file, it is safe to write the directory entry that * claims the inode once the referenced inode has been written. Since * writing the inode clears the bitmap dependencies, the DEPCOMPLETE flag * in the diradd can be set unconditionally when creating a file. When * creating a directory, there are two additional dependencies described by * mkdir structures (see their description below). When these dependencies * are resolved the DEPCOMPLETE flag is set in the diradd structure. * If there are multiple links created to the same inode, there will be * a separate diradd structure created for each link. The diradd is * linked onto the pg_diraddhd list of the pagedep for the directory * page that contains the entry. When a directory page is written, * the pg_diraddhd list is traversed to rollback any entries that are * not yet ready to be written to disk. If a directory entry is being * changed (by rename) rather than added, the DIRCHG flag is set and * the da_previous entry points to the entry that will be "removed" * once the new entry has been committed. During rollback, entries * with da_previous are replaced with the previous inode number rather * than zero. * * The overlaying of da_pagedep and da_previous is done to keep the * structure down to 32 bytes in size on a 32-bit machine. If a * da_previous entry is present, the pointer to its pagedep is available * in the associated dirrem entry. If the DIRCHG flag is set, the * da_previous entry is valid; if not set the da_pagedep entry is valid. * The DIRCHG flag never changes; it is set when the structure is created * if appropriate and is never cleared. */ struct diradd { struct worklist da_list; /* id_inowait or id_pendinghd list */ # define da_state da_list.wk_state /* state of the new directory entry */ LIST_ENTRY(diradd) da_pdlist; /* pagedep holding directory block */ doff_t da_offset; /* offset of new dir entry in dir blk */ ino_t da_newinum; /* inode number for the new dir entry */ union { struct dirrem *dau_previous; /* entry being replaced in dir change */ struct pagedep *dau_pagedep; /* pagedep dependency for addition */ } da_un; }; #define da_previous da_un.dau_previous #define da_pagedep da_un.dau_pagedep /* * Two "mkdir" structures are needed to track the additional dependencies * associated with creating a new directory entry. Normally a directory * addition can be committed as soon as the newly referenced inode has been * written to disk with its increased link count. When a directory is * created there are two additional dependencies: writing the directory * data block containing the "." and ".." entries (MKDIR_BODY) and writing * the parent inode with the increased link count for ".." (MKDIR_PARENT). * These additional dependencies are tracked by two mkdir structures that * reference the associated "diradd" structure. When they have completed, * they set the DEPCOMPLETE flag on the diradd so that it knows that its * extra dependencies have been completed. The md_state field is used only * to identify which type of dependency the mkdir structure is tracking. * It is not used in the mainline code for any purpose other than consistency * checking. All the mkdir structures in the system are linked together on * a list. This list is needed so that a diradd can find its associated * mkdir structures and deallocate them if it is prematurely freed (as for * example if a mkdir is immediately followed by a rmdir of the same directory). * Here, the free of the diradd must traverse the list to find the associated * mkdir structures that reference it. The deletion would be faster if the * diradd structure were simply augmented to have two pointers that referenced * the associated mkdir's. However, this would increase the size of the diradd * structure from 32 to 64-bits to speed a very infrequent operation. */ struct mkdir { struct worklist md_list; /* id_inowait or buffer holding dir */ # define md_state md_list.wk_state /* type: MKDIR_PARENT or MKDIR_BODY */ struct diradd *md_diradd; /* associated diradd */ struct buf *md_buf; /* MKDIR_BODY: buffer holding dir */ LIST_ENTRY(mkdir) md_mkdirs; /* list of all mkdirs */ }; LIST_HEAD(mkdirlist, mkdir) mkdirlisthd; /* * A "dirrem" structure describes an operation to decrement the link * count on an inode. The dirrem structure is attached to the pg_dirremhd * list of the pagedep for the directory page that contains the entry. * It is processed after the directory page with the deleted entry has * been written to disk. * * The overlaying of dm_pagedep and dm_dirinum is done to keep the * structure down to 32 bytes in size on a 32-bit machine. It works * because they are never used concurrently. */ struct dirrem { struct worklist dm_list; /* delayed worklist */ # define dm_state dm_list.wk_state /* state of the old directory entry */ LIST_ENTRY(dirrem) dm_next; /* pagedep's list of dirrem's */ struct mount *dm_mnt; /* associated mount point */ ino_t dm_oldinum; /* inum of the removed dir entry */ union { struct pagedep *dmu_pagedep; /* pagedep dependency for remove */ ino_t dmu_dirinum; /* parent inode number (for rmdir) */ } dm_un; }; #define dm_pagedep dm_un.dmu_pagedep #define dm_dirinum dm_un.dmu_dirinum /* * A "newdirblk" structure tracks the progress of a newly allocated * directory block from its creation until it is claimed by its on-disk * inode. When a block is allocated to a directory, an fsync of a file * whose name is within that block must ensure not only that the block * containing the file name has been written, but also that the on-disk * inode references that block. When a new directory block is created, * we allocate a newdirblk structure which is linked to the associated * allocdirect (on its ad_newdirblk list). When the allocdirect has been * satisfied, the newdirblk structure is moved to the inodedep id_bufwait * list of its directory to await the inode being written. When the inode * is written, the directory entries are fully committed and can be * deleted from their pagedep->id_pendinghd and inodedep->id_pendinghd * lists. Note that we could track directory blocks allocated to indirect * blocks using a similar scheme with the allocindir structures. Rather * than adding this level of complexity, we simply write those newly * allocated indirect blocks synchronously as such allocations are rare. */ struct newdirblk { struct worklist db_list;/* id_inowait or pg_newdirblk */ # define db_state db_list.wk_state /* unused */ struct pagedep *db_pagedep;/* associated pagedep */ }; makefs/src/sys/ufs/ufs/dinode.h010064400000000000000000000146631314214547500137050ustar00/* $OpenBSD: dinode.h,v 1.10 2005/06/18 18:09:43 millert Exp $ */ /* $NetBSD: dinode.h,v 1.7 1995/06/15 23:22:48 cgd Exp $ */ /* * Copyright (c) 1982, 1989, 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. * * @(#)dinode.h 8.9 (Berkeley) 3/29/95 */ #ifndef _UFS_DINODE_H_ #define _UFS_DINODE_H_ /* * The root inode is the root of the filesystem. Inode 0 can't be used for * normal purposes and historically bad blocks were linked to inode 1, thus * the root inode is 2. (Inode 1 is no longer used for this purpose, however * numerous dump tapes make this assumption, so we are stuck with it). */ #define ROOTINO ((ino_t)2) /* * A dinode contains all the meta-data associated with a UFS file. * This structure defines the on-disk format of a dinode. Since * this structure describes an on-disk structure, all its fields * are defined by types with precise widths. */ typedef int32_t ufs_daddr_t; typedef int32_t ufs1_daddr_t; typedef int64_t ufs2_daddr_t; typedef int64_t ufs_lbn_t; typedef int64_t ufs_time_t; #define NXADDR 2 /* External addresses in inode */ #define NDADDR 12 /* Direct addresses in inode. */ #define NIADDR 3 /* Indirect addresses in inode. */ struct ufs1_dinode { u_int16_t di_mode; /* 0: IFMT, permissions; see below. */ int16_t di_nlink; /* 2: File link count. */ union { u_int16_t oldids[2]; /* 4: Ffs: old user and group ids. */ u_int32_t inumber; /* 4: Lfs: inode number. */ } di_u; u_int64_t di_size; /* 8: File byte count. */ int32_t di_atime; /* 16: Last access time. */ int32_t di_atimensec; /* 20: Last access time. */ int32_t di_mtime; /* 24: Last modified time. */ int32_t di_mtimensec; /* 28: Last modified time. */ int32_t di_ctime; /* 32: Last inode change time. */ int32_t di_ctimensec; /* 36: Last inode change time. */ ufs1_daddr_t di_db[NDADDR]; /* 40: Direct disk blocks. */ ufs1_daddr_t di_ib[NIADDR]; /* 88: Indirect disk blocks. */ u_int32_t di_flags; /* 100: Status flags (chflags). */ int32_t di_blocks; /* 104: Blocks actually held. */ int32_t di_gen; /* 108: Generation number. */ u_int32_t di_uid; /* 112: File owner. */ u_int32_t di_gid; /* 116: File group. */ int32_t di_spare[2]; /* 120: Reserved; currently unused */ }; struct ufs2_dinode { u_int16_t di_mode; /* 0: IFMT, permissions; see below. */ int16_t di_nlink; /* 2: File link count. */ u_int32_t di_uid; /* 4: File owner. */ u_int32_t di_gid; /* 8: File group. */ u_int32_t di_blksize; /* 12: Inode blocksize. */ u_int64_t di_size; /* 16: File byte count. */ u_int64_t di_blocks; /* 24: Bytes actually held. */ ufs_time_t di_atime; /* 32: Last access time. */ ufs_time_t di_mtime; /* 40: Last modified time. */ ufs_time_t di_ctime; /* 48: Last inode change time. */ ufs_time_t di_birthtime; /* 56: Inode creation time. */ int32_t di_mtimensec; /* 64: Last modified time. */ int32_t di_atimensec; /* 68: Last access time. */ int32_t di_ctimensec; /* 72: Last inode change time. */ int32_t di_birthnsec; /* 76: Inode creation time. */ int32_t di_gen; /* 80: Generation number. */ u_int32_t di_kernflags; /* 84: Kernel flags. */ u_int32_t di_flags; /* 88: Status flags (chflags). */ int32_t di_extsize; /* 92: External attributes block. */ ufs2_daddr_t di_extb[NXADDR];/* 96: External attributes block. */ ufs2_daddr_t di_db[NDADDR]; /* 112: Direct disk blocks. */ ufs2_daddr_t di_ib[NIADDR]; /* 208: Indirect disk blocks. */ int64_t di_spare[3]; /* 232: Reserved; currently unused */ }; /* * The di_db fields may be overlaid with other information for * file types that do not have associated disk storage. Block * and character devices overlay the first data block with their * dev_t value. Short symbolic links place their path in the * di_db area. */ #define di_inumber di_u.inumber #define di_ogid di_u.oldids[1] #define di_ouid di_u.oldids[0] #define di_rdev di_db[0] #define di_shortlink di_db #define MAXSYMLINKLEN ((NDADDR + NIADDR) * sizeof(ufs1_daddr_t)) /* File permissions. */ #define IEXEC 0000100 /* Executable. */ #define IWRITE 0000200 /* Writeable. */ #define IREAD 0000400 /* Readable. */ #define ISVTX 0001000 /* Sticky bit. */ #define ISGID 0002000 /* Set-gid. */ #define ISUID 0004000 /* Set-uid. */ /* File types. */ #define IFMT 0170000 /* Mask of file type. */ #define IFIFO 0010000 /* Named pipe (fifo). */ #define IFCHR 0020000 /* Character device. */ #define IFDIR 0040000 /* Directory file. */ #define IFBLK 0060000 /* Block device. */ #define IFREG 0100000 /* Regular file. */ #define IFLNK 0120000 /* Symbolic link. */ #define IFSOCK 0140000 /* UNIX domain socket. */ #define IFWHT 0160000 /* Whiteout. */ #endif /* _UFS_DINODE_H_ */ makefs/src/sys/ufs/ufs/dir.h010064400000000000000000000136631026206255500132150ustar00/* $OpenBSD: dir.h,v 1.10 2005/06/18 18:09:43 millert Exp $ */ /* $NetBSD: dir.h,v 1.8 1996/03/09 19:42:41 scottr Exp $ */ /* * Copyright (c) 1982, 1986, 1989, 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. * * @(#)dir.h 8.4 (Berkeley) 8/10/94 */ #ifndef _DIR_H_ #define _DIR_H_ /* * Theoretically, directories can be more than 2Gb in length, however, in * practice this seems unlikely. So, we define the type doff_t as a 32-bit * quantity to keep down the cost of doing lookup on a 32-bit machine. */ #define doff_t int32_t #define MAXDIRSIZE (0x7fffffff) /* * A directory consists of some number of blocks of DIRBLKSIZ * bytes, where DIRBLKSIZ is chosen such that it can be transferred * to disk in a single atomic operation (e.g. 512 bytes on most machines). * * Each DIRBLKSIZ byte block contains some number of directory entry * structures, which are of variable length. Each directory entry has * a struct direct at the front of it, containing its inode number, * the length of the entry, and the length of the name contained in * the entry. These are followed by the name padded to a 4 byte boundary * with null bytes. All names are guaranteed null terminated. * The maximum length of a name in a directory is MAXNAMLEN. * * The macro DIRSIZ(fmt, dp) gives the amount of space required to represent * a directory entry. Free space in a directory is represented by * entries which have dp->d_reclen > DIRSIZ(fmt, dp). All DIRBLKSIZ bytes * in a directory block are claimed by the directory entries. This * usually results in the last entry in a directory having a large * dp->d_reclen. When entries are deleted from a directory, the * space is returned to the previous entry in the same directory * block by increasing its dp->d_reclen. If the first entry of * a directory block is free, then its dp->d_ino is set to 0. * Entries other than the first in a directory do not normally have * dp->d_ino set to 0. */ #define DIRBLKSIZ DEV_BSIZE #define MAXNAMLEN 255 struct direct { u_int32_t d_ino; /* inode number of entry */ u_int16_t d_reclen; /* length of this record */ u_int8_t d_type; /* file type, see below */ u_int8_t d_namlen; /* length of string in d_name */ char d_name[MAXNAMLEN + 1];/* name with length <= MAXNAMLEN */ }; /* * File types */ #define DT_UNKNOWN 0 #define DT_FIFO 1 #define DT_CHR 2 #define DT_DIR 4 #define DT_BLK 6 #define DT_REG 8 #define DT_LNK 10 #define DT_SOCK 12 /* * Convert between stat structure types and directory types. */ #define IFTODT(mode) (((mode) & 0170000) >> 12) #define DTTOIF(dirtype) ((dirtype) << 12) /* * The DIRSIZ macro gives the minimum record length which will hold * the directory entry. This requires the amount of space in struct direct * without the d_name field, plus enough space for the name with a terminating * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary. */ #define DIRECTSIZ(namlen) \ (((int)&((struct direct *)0)->d_name + \ ((namlen)+1)*sizeof(((struct direct *)0)->d_name[0]) + 3) & ~3) #if (BYTE_ORDER == LITTLE_ENDIAN) #define DIRSIZ(oldfmt, dp) \ ((oldfmt) ? \ ((sizeof(struct direct) - (MAXNAMLEN+1)) + (((dp)->d_type+1 + 3) &~ 3)) : \ ((sizeof(struct direct) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))) #else #define DIRSIZ(oldfmt, dp) \ ((sizeof(struct direct) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3)) #endif #define OLDDIRFMT 1 #define NEWDIRFMT 0 /* * Template for manipulating directories. Should use struct direct's, * but the name field is MAXNAMLEN - 1, and this just won't do. */ struct dirtemplate { u_int32_t dot_ino; int16_t dot_reclen; u_int8_t dot_type; u_int8_t dot_namlen; char dot_name[4]; /* must be multiple of 4 */ u_int32_t dotdot_ino; int16_t dotdot_reclen; u_int8_t dotdot_type; u_int8_t dotdot_namlen; char dotdot_name[4]; /* ditto */ }; /* * This is the old format of directories, sanz type element. */ struct odirtemplate { u_int32_t dot_ino; int16_t dot_reclen; u_int16_t dot_namlen; char dot_name[4]; /* must be multiple of 4 */ u_int32_t dotdot_ino; int16_t dotdot_reclen; u_int16_t dotdot_namlen; char dotdot_name[4]; /* ditto */ }; #endif /* !_DIR_H_ */ makefs/src/sys/ufs/ufs/dirhash.h010064400000000000000000000124201020120120700140250ustar00/* $OpenBSD: dirhash.h,v 1.3 2004/02/02 19:34:39 tedu Exp $ */ /* * Copyright (c) 2001 Ian Dowse. 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. * * $FreeBSD: src/sys/ufs/ufs/dirhash.h,v 1.4 2003/01/01 18:48:59 schweikh Exp $ */ #ifndef _UFS_UFS_DIRHASH_H_ #define _UFS_UFS_DIRHASH_H_ #include /* * For fast operations on large directories, we maintain a hash * that maps the file name to the offset of the directory entry within * the directory file. * * The hashing uses a dumb spillover to the next free slot on * collisions, so we must keep the utilisation low to avoid * long linear searches. Deleted entries that are not the last * in a chain must be marked DIRHASH_DEL. * * We also maintain information about free space in each block * to speed up creations. */ #define DIRHASH_EMPTY (-1) /* entry unused */ #define DIRHASH_DEL (-2) /* deleted entry; may be part of chain */ #define DIRALIGN 4 #define DH_NFSTATS (DIRECTSIZ(MAXNAMLEN + 1) / DIRALIGN) /* max DIRALIGN words in a directory entry */ /* * Dirhash uses a score mechanism to achieve a hybrid between a * least-recently-used and a least-often-used algorithm for entry * recycling. The score is incremented when a directory is used, and * decremented when the directory is a candidate for recycling. When * the score reaches zero, the hash is recycled. Hashes are linked * together on a TAILQ list, and hashes with higher scores filter * towards the tail (most recently used) end of the list. * * New hash entries are given an inital score of DH_SCOREINIT and are * placed at the most-recently-used end of the list. This helps a lot * in the worst-case case scenario where every directory access is * to a directory that is not hashed (i.e. the working set of hash * candidates is much larger than the configured memry limit). In this * case it limits the number of hash builds to 1/DH_SCOREINIT of the * number of accesses. */ #define DH_SCOREINIT 8 /* initial dh_score when dirhash built */ #define DH_SCOREMAX 64 /* max dh_score value */ /* * The main hash table has 2 levels. It is an array of pointers to * blocks of DH_NBLKOFF offsets. */ #define DH_BLKOFFSHIFT 8 #define DH_NBLKOFF (1 << DH_BLKOFFSHIFT) #define DH_BLKOFFMASK (DH_NBLKOFF - 1) #define DH_ENTRY(dh, slot) \ ((dh)->dh_hash[(slot) >> DH_BLKOFFSHIFT][(slot) & DH_BLKOFFMASK]) struct dirhash { struct rwlock dh_mtx; /* protects all fields except dh_list */ doff_t **dh_hash; /* the hash array (2-level) */ int dh_narrays; /* number of entries in dh_hash */ int dh_hlen; /* total slots in the 2-level hash array */ int dh_hused; /* entries in use */ /* Free space statistics. XXX assumes DIRBLKSIZ is 512. */ u_int8_t *dh_blkfree; /* free DIRALIGN words in each dir block */ int dh_nblk; /* size of dh_blkfree array */ int dh_dirblks; /* number of DIRBLKSIZ blocks in dir */ int dh_firstfree[DH_NFSTATS + 1]; /* first blk with N words free */ int dh_seqopt; /* sequential access optimisation enabled */ doff_t dh_seqoff; /* sequential access optimisation offset */ int dh_score; /* access count for this dirhash */ int dh_onlist; /* true if on the ufsdirhash_list chain */ /* Protected by ufsdirhash_mtx. */ TAILQ_ENTRY(dirhash) dh_list; /* chain of all dirhashes */ }; extern int ufs_mindirhashsize; extern int ufs_dirhashmaxmem; extern int ufs_dirhashmem; /* * Dirhash functions. */ void ufsdirhash_init(void); void ufsdirhash_uninit(void); int ufsdirhash_build(struct inode *); doff_t ufsdirhash_findfree(struct inode *, int, int *); doff_t ufsdirhash_enduseful(struct inode *); int ufsdirhash_lookup(struct inode *, char *, int, doff_t *, struct buf **, doff_t *); void ufsdirhash_newblk(struct inode *, doff_t); void ufsdirhash_add(struct inode *, struct direct *, doff_t); void ufsdirhash_remove(struct inode *, struct direct *, doff_t); void ufsdirhash_move(struct inode *, struct direct *, doff_t, doff_t); void ufsdirhash_dirtrunc(struct inode *, doff_t); void ufsdirhash_free(struct inode *); void ufsdirhash_checkblock(struct inode *, char *, doff_t); #endif /* !_UFS_UFS_DIRHASH_H_ */ makefs/src/sys/ufs/ufs/extattr.h010064400000000000000000000076731314214547500141410ustar00/* $OpenBSD: extattr.h,v 1.1 2002/02/22 20:37:46 drahn Exp $ */ /*- * Copyright (c) 1999, 2000, 2001 Robert N. M. Watson * 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. * * $FreeBSD: extattr.h,v 1.14 2001/09/12 08:38:10 julian Exp $ */ /* * TrustedBSD Project - extended attribute support for UFS-like filesystems */ #ifndef _UFS_UFS_EXTATTR_H_ #define _UFS_UFS_EXTATTR_H_ #define UFS_EXTATTR_MAGIC 0x00b5d5ec #define UFS_EXTATTR_VERSION 0x00000003 #define UFS_EXTATTR_FSROOTSUBDIR ".attribute" #define UFS_EXTATTR_SUBDIR_SYSTEM "system" #define UFS_EXTATTR_SUBDIR_USER "user" #define UFS_EXTATTR_MAXEXTATTRNAME 65 /* including null */ #define UFS_EXTATTR_ATTR_FLAG_INUSE 0x00000001 /* attr has been set */ #define UFS_EXTATTR_PERM_KERNEL 0x00000000 #define UFS_EXTATTR_PERM_ROOT 0x00000001 #define UFS_EXTATTR_PERM_OWNER 0x00000002 #define UFS_EXTATTR_PERM_ANYONE 0x00000003 #define UFS_EXTATTR_UEPM_INITIALIZED 0x00000001 #define UFS_EXTATTR_UEPM_STARTED 0x00000002 #define UFS_EXTATTR_CMD_START 0x00000001 #define UFS_EXTATTR_CMD_STOP 0x00000002 #define UFS_EXTATTR_CMD_ENABLE 0x00000003 #define UFS_EXTATTR_CMD_DISABLE 0x00000004 struct ufs_extattr_fileheader { u_int uef_magic; /* magic number for sanity checking */ u_int uef_version; /* version of attribute file */ u_int uef_size; /* size of attributes, w/o header */ }; struct ufs_extattr_header { u_int ueh_flags; /* flags for attribute */ u_int ueh_len; /* local defined length; <= uef_size */ u_int32_t ueh_i_gen; /* generation number for sanity */ /* data follows the header */ }; #ifdef _KERNEL #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_EXTATTR); #endif struct vnode; LIST_HEAD(ufs_extattr_list_head, ufs_extattr_list_entry); struct ufs_extattr_list_entry { LIST_ENTRY(ufs_extattr_list_entry) uele_entries; struct ufs_extattr_fileheader uele_fileheader; int uele_attrnamespace; char uele_attrname[UFS_EXTATTR_MAXEXTATTRNAME]; struct vnode *uele_backing_vnode; }; struct lock; struct ucred; struct ufs_extattr_per_mount { struct lock uepm_lock; struct ufs_extattr_list_head uepm_list; struct ucred *uepm_ucred; int uepm_flags; }; void ufs_extattr_uepm_init(struct ufs_extattr_per_mount *uepm); void ufs_extattr_uepm_destroy(struct ufs_extattr_per_mount *uepm); int ufs_extattr_start(struct mount *mp, struct proc *p); int ufs_extattr_autostart(struct mount *mp, struct proc *p); int ufs_extattr_stop(struct mount *mp, struct proc *p); int ufs_extattrctl(struct mount *mp, int cmd, struct vnode *filename, int attrnamespace, const char *attrname, struct proc *p); void ufs_extattr_vnode_inactive(struct vnode *vp, struct proc *p); int ufs_vop_getextattr(void *); int ufs_vop_setextattr(void *); #endif /* !_KERNEL */ #endif /* !_UFS_UFS_EXTATTR_H_ */ makefs/src/sys/ufs/ufs/inode.h010064400000000000000000000267701057714131700135430ustar00/* $OpenBSD: inode.h,v 1.27 2004/07/13 21:04:29 millert Exp $ */ /* $NetBSD: inode.h,v 1.8 1995/06/15 23:22:50 cgd Exp $ */ /* * Copyright (c) 1982, 1989, 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. * * @(#)inode.h 8.5 (Berkeley) 7/8/94 */ #include #include #include #include /* * Per-filesystem inode extensions. */ struct ext2fs_inode_ext { ufs1_daddr_t ext2fs_last_lblk; /* last logical block allocated */ ufs1_daddr_t ext2fs_last_blk; /* last block allocated on disk */ u_int32_t ext2fs_effective_uid; /* effective inode uid */ u_int32_t ext2fs_effective_gid; /* effective inode gid */ }; /* * The inode is used to describe each active (or recently active) file in the * UFS filesystem. It is composed of two types of information. The first part * is the information that is needed only while the file is active (such as * the identity of the file and linkage to speed its lookup). The second part * is * the permanent meta-data associated with the file which is read in * from the permanent dinode from long term storage when the file becomes * active, and is put back when the file is no longer being used. */ struct inode { LIST_ENTRY(inode) i_hash; /* Hash chain */ struct vnode *i_vnode;/* Vnode associated with this inode. */ struct ufsmount *i_ump; u_int32_t i_flag; /* flags, see below */ dev_t i_dev; /* Device associated with the inode. */ ino_t i_number; /* The identity of the inode. */ int i_effnlink; /* i_nlink when I/O completes */ union { /* Associated filesystem. */ struct fs *fs; /* FFS */ struct lfs *lfs; /* LFS */ struct m_ext2fs *e2fs; /* EXT2FS */ } inode_u; #define i_fs inode_u.fs #define i_lfs inode_u.lfs #define i_e2fs inode_u.e2fs struct cluster_info i_ci; struct dquot *i_dquot[MAXQUOTAS]; /* Dquot structures. */ u_quad_t i_modrev; /* Revision level for NFS lease. */ struct lockf *i_lockf;/* Head of byte-level lock list. */ struct lock i_lock; /* Inode lock */ /* * Side effects; used during directory lookup. */ int32_t i_count; /* Size of free slot in directory. */ doff_t i_endoff; /* End of useful stuff in directory. */ doff_t i_diroff; /* Offset in dir, where we found last entry. */ doff_t i_offset; /* Offset of free space in directory. */ ino_t i_ino; /* Inode number of found directory. */ u_int32_t i_reclen; /* Size of found directory entry. */ /* * Inode extensions */ union { /* Other extensions could go here... */ struct ext2fs_inode_ext e2fs; struct dirhash *dirhash; } inode_ext; #define i_e2fs_last_lblk inode_ext.e2fs.ext2fs_last_lblk #define i_e2fs_last_blk inode_ext.e2fs.ext2fs_last_blk #define i_e2fs_uid inode_ext.e2fs.ext2fs_effective_uid #define i_e2fs_gid inode_ext.e2fs.ext2fs_effective_gid #define i_dirhash inode_ext.dirhash /* * The on-disk dinode itself. */ union { struct ufs1_dinode ffs1_din; struct ext2fs_dinode e2fs_din; } dinode_u; #define i_din1 dinode_u.ffs1_din #define i_e2din dinode_u.e2fs_din struct inode_vtbl *i_vtbl; }; struct inode_vtbl { int (* iv_truncate)(struct inode *, off_t, int, struct ucred *); int (* iv_update)(struct inode *, struct timespec *, struct timespec *, int waitfor); int (* iv_inode_alloc)(struct inode *, mode_t mode, struct ucred *, struct vnode **); int (* iv_inode_free)(struct inode *, ino_t ino, mode_t mode); int (* iv_buf_alloc)(struct inode *, off_t, int, struct ucred *, int, struct buf **); int (* iv_bufatoff)(struct inode *, off_t offset, char **res, struct buf **bpp); }; #define UFS_TRUNCATE(ip, off, flags, cred) \ ((ip)->i_vtbl->iv_truncate)((ip), (off), (flags), (cred)) #define UFS_UPDATE(ip, sync) \ ((ip)->i_vtbl->iv_update)((ip), NULL, NULL, (sync)) #define UFS_UPDATE2(ip, atime, mtime, sync) \ ((ip)->i_vtbl->iv_update)((ip), (atime), (mtime), (sync)) #define UFS_INODE_ALLOC(pip, mode, cred, vpp) \ ((pip)->i_vtbl->iv_inode_alloc)((pip), (mode), (cred), (vpp)) #define UFS_INODE_FREE(pip, ino, mode) \ ((pip)->i_vtbl->iv_inode_free)((pip), (ino), (mode)) #define UFS_BUF_ALLOC(ip, startoffset, size, cred, flags, bpp) \ ((ip)->i_vtbl->iv_buf_alloc)((ip), (startoffset), (size), (cred), \ (flags), (bpp)) #define UFS_BUFATOFF(ip, offset, res, bpp) \ ((ip)->i_vtbl->iv_bufatoff)((ip), (offset), (res), (bpp)) #define i_ffs_atime i_din1.di_atime #define i_ffs_atimensec i_din1.di_atimensec #define i_ffs_blocks i_din1.di_blocks #define i_ffs_ctime i_din1.di_ctime #define i_ffs_ctimensec i_din1.di_ctimensec #define i_ffs_db i_din1.di_db #define i_ffs_flags i_din1.di_flags #define i_ffs_gen i_din1.di_gen #define i_ffs_gid i_din1.di_gid #define i_ffs_ib i_din1.di_ib #define i_ffs_mode i_din1.di_mode #define i_ffs_mtime i_din1.di_mtime #define i_ffs_mtimensec i_din1.di_mtimensec #define i_ffs_nlink i_din1.di_nlink #define i_ffs_rdev i_din1.di_rdev #define i_ffs_shortlink i_din1.di_shortlink #define i_ffs_size i_din1.di_size #define i_ffs_uid i_din1.di_uid #define i_size i_din1.di_size #ifndef _KERNEL /* * These are here purely for backwards compatibility for userland. * They allow direct references to FFS structures using the old names. */ #define i_atime i_din1.di_atime #define i_atimensec i_din1.di_atimensec #define i_blocks i_din1.di_blocks #define i_ctime i_din1.di_ctime #define i_ctimensec i_din1.di_ctimensec #define i_db i_din1.di_db #define i_flags i_din1.di_flags #define i_gen i_din1.di_gen #define i_gid i_din1.di_gid #define i_ib i_din1.di_ib #define i_mode i_din1.di_mode #define i_mtime i_din1.di_mtime #define i_mtimensec i_din1.di_mtimensec #define i_nlink i_din1.di_nlink #define i_rdev i_din1.di_rdev #define i_shortlink i_din1.di_shortlink #define i_size i_din1.di_size #define i_uid i_din1.di_uid #endif /* _KERNEL */ #define i_e2fs_mode i_e2din.e2di_mode #define i_e2fs_size i_e2din.e2di_size #define i_e2fs_atime i_e2din.e2di_atime #define i_e2fs_ctime i_e2din.e2di_ctime #define i_e2fs_mtime i_e2din.e2di_mtime #define i_e2fs_dtime i_e2din.e2di_dtime #define i_e2fs_nlink i_e2din.e2di_nlink #define i_e2fs_nblock i_e2din.e2di_nblock #define i_e2fs_flags i_e2din.e2di_flags #define i_e2fs_blocks i_e2din.e2di_blocks #define i_e2fs_gen i_e2din.e2di_gen #define i_e2fs_facl i_e2din.e2di_facl #define i_e2fs_dacl i_e2din.e2di_dacl #define i_e2fs_faddr i_e2din.e2di_faddr #define i_e2fs_nfrag i_e2din.e2di_nfrag #define i_e2fs_fsize i_e2din.e2di_fsize #define i_e2fs_uid_low i_e2din.e2di_uid_low #define i_e2fs_gid_low i_e2din.e2di_gid_low #define i_e2fs_uid_high i_e2din.e2di_uid_high #define i_e2fs_gid_high i_e2din.e2di_gid_high /* These flags are kept in i_flag. */ #define IN_ACCESS 0x0001 /* Access time update request. */ #define IN_CHANGE 0x0002 /* Inode change time update request. */ #define IN_UPDATE 0x0004 /* Modification time update request */ #define IN_MODIFIED 0x0008 /* Inode has been modified. */ #define IN_RENAME 0x0010 /* Inode is being renamed. */ #define IN_SHLOCK 0x0020 /* File has shared lock. */ #define IN_EXLOCK 0x0040 /* File has exclusive lock. */ #define i_devvp i_ump->um_devvp #ifdef _KERNEL /* * The DIP macro is used to access fields in the dinode that are * not cached in the inode itself. */ #define DIP(ip, field) \ (((ip)->i_ump->um_fstype == UM_UFS1) ? \ (ip)->i_din1->d##field : (ip)->i_din2->d##field) #if 0 #define MAXSYMLINKLEN(ip) \ ((ip)->i_ump->um_fstype == UM_UFS1) ? \ ((NDADDR + NIADDR) * sizeof(ufs1_daddr_t)) : \ ((NDADDR + NIADDR) * sizeof(ufs2_daddr_t)) #define SHORTLINK(ip) \ (((ip)->i_ump->um_fstype == UM_UFS1) ? \ (caddr_t)(ip)->i_din1->di_db : (caddr_t)(ip)->i_din2->di_db) #endif /* * Structure used to pass around logical block paths generated by * ufs_getlbns and used by truncate and bmap code. */ struct indir { daddr_t in_lbn; /* Logical block number. */ int in_off; /* Offset in buffer. */ int in_exists; /* Flag if the block exists. */ }; /* Convert between inode pointers and vnode pointers. */ #define VTOI(vp) ((struct inode *)(vp)->v_data) #define ITOV(ip) ((ip)->i_vnode) #define FFS_ITIMES(ip, t1, t2) { \ if ((ip)->i_flag & (IN_ACCESS | IN_CHANGE | IN_UPDATE)) { \ (ip)->i_flag |= IN_MODIFIED; \ if ((ip)->i_flag & IN_ACCESS) \ (ip)->i_ffs_atime = (t1)->tv_sec; \ if ((ip)->i_flag & IN_UPDATE) { \ (ip)->i_ffs_mtime = (t2)->tv_sec; \ (ip)->i_modrev++; \ } \ if ((ip)->i_flag & IN_CHANGE) \ (ip)->i_ffs_ctime = time.tv_sec; \ (ip)->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE); \ } \ } #define EXT2FS_ITIMES(ip, t1, t2) { \ if ((ip)->i_flag & (IN_ACCESS | IN_CHANGE | IN_UPDATE)) { \ (ip)->i_flag |= IN_MODIFIED; \ if ((ip)->i_flag & IN_ACCESS) \ (ip)->i_e2fs_atime = (t1)->tv_sec; \ if ((ip)->i_flag & IN_UPDATE) { \ (ip)->i_e2fs_mtime = (t2)->tv_sec; \ (ip)->i_modrev++; \ } \ if ((ip)->i_flag & IN_CHANGE) \ (ip)->i_e2fs_ctime = time.tv_sec; \ (ip)->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE); \ } \ } #define ITIMES(ip, t1, t2) { \ if (IS_EXT2_VNODE((ip)->i_vnode)) { \ EXT2FS_ITIMES(ip, t1, t2); \ } else { \ FFS_ITIMES(ip, t1, t2); \ } \ } /* Determine if soft dependencies are being done */ #ifdef FFS_SOFTUPDATES #define DOINGSOFTDEP(vp) ((vp)->v_mount->mnt_flag & MNT_SOFTDEP) #else #define DOINGSOFTDEP(vp) (0) #endif #define DOINGASYNC(vp) ((vp)->v_mount->mnt_flag & MNT_ASYNC) /* This overlays the fid structure (see mount.h). */ struct ufid { u_int16_t ufid_len; /* Length of structure. */ u_int16_t ufid_pad; /* Force 32-bit alignment. */ ino_t ufid_ino; /* File number (ino). */ int32_t ufid_gen; /* Generation number. */ }; #endif /* _KERNEL */ makefs/src/sys/ufs/ufs/quota.h010064400000000000000000000135421020120120700135420ustar00/* $OpenBSD: quota.h,v 1.8 2003/06/02 23:28:23 millert Exp $ */ /* $NetBSD: quota.h,v 1.6 1995/03/26 20:38:17 jtc Exp $ */ /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Robert Elz at The University of Melbourne. * * 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. * * @(#)quota.h 8.3 (Berkeley) 8/19/94 */ #ifndef _QUOTA_ #define _QUOTA_ /* * Definitions for disk quotas imposed on the average user * (big brother finally hits UNIX). * * The following constants define the amount of time given a user before the * soft limits are treated as hard limits (usually resulting in an allocation * failure). The timer is started when the user crosses their soft limit, it * is reset when they go below their soft limit. */ #define MAX_IQ_TIME (7*24*60*60) /* seconds in 1 week */ #define MAX_DQ_TIME (7*24*60*60) /* seconds in 1 week */ /* * The following constants define the usage of the quota file array in the * ufsmount structure and dquot array in the inode structure. The semantics * of the elements of these arrays are defined in the routine getinoquota; * the remainder of the quota code treats them generically and need not be * inspected when changing the size of the array. */ #define MAXQUOTAS 2 #define USRQUOTA 0 /* element used for user quotas */ #define GRPQUOTA 1 /* element used for group quotas */ /* * Definitions for the default names of the quotas files. */ #define INITQFNAMES { \ "user", /* USRQUOTA */ \ "group", /* GRPQUOTA */ \ "undefined", \ } #define QUOTAFILENAME "quota" #define QUOTAGROUP "operator" /* * Command definitions for the 'quotactl' system call. The commands are * broken into a main command defined below and a subcommand that is used * to convey the type of quota that is being manipulated (see above). */ #define SUBCMDMASK 0x00ff #define SUBCMDSHIFT 8 #define QCMD(cmd, type) (((cmd) << SUBCMDSHIFT) | ((type) & SUBCMDMASK)) #define Q_QUOTAON 0x0100 /* enable quotas */ #define Q_QUOTAOFF 0x0200 /* disable quotas */ #define Q_GETQUOTA 0x0300 /* get limits and usage */ #define Q_SETQUOTA 0x0400 /* set limits and usage */ #define Q_SETUSE 0x0500 /* set usage */ #define Q_SYNC 0x0600 /* sync disk copy of a filesystems quotas */ /* * The following structure defines the format of the disk quota file * (as it appears on disk) - the file is an array of these structures * indexed by user or group number. The setquota system call establishes * the vnode for each quota file (a pointer is retained in the ufsmount * structure). */ struct dqblk { u_int32_t dqb_bhardlimit; /* absolute limit on disk blks alloc */ u_int32_t dqb_bsoftlimit; /* preferred limit on disk blks */ u_int32_t dqb_curblocks; /* current block count */ u_int32_t dqb_ihardlimit; /* maximum # allocated inodes + 1 */ u_int32_t dqb_isoftlimit; /* preferred inode limit */ u_int32_t dqb_curinodes; /* current # allocated inodes */ time_t dqb_btime; /* time limit for excessive disk use */ time_t dqb_itime; /* time limit for excessive files */ }; #ifdef _KERNEL /* * Flags to ufs_quota_{alloc,free}_{blocks,inode}2 */ enum ufs_quota_flags { UFS_QUOTA_NOUID = 0x1, /* Don't change UID quota */ UFS_QUOTA_NOGID = 0x2, /* Don't change GID quota */ UFS_QUOTA_FORCE = 0x1000 /* don't check limits - just change it */ }; /* Change GID */ #include struct dquot; struct inode; struct mount; struct proc; struct ucred; struct ufsmount; struct vnode; __BEGIN_DECLS #define ufs_quota_alloc_blocks(i, c, cr) ufs_quota_alloc_blocks2(i, c, cr, 0) #define ufs_quota_free_blocks(i, c, cr) ufs_quota_free_blocks2(i, c, cr, 0) #define ufs_quota_alloc_inode(i, cr) ufs_quota_alloc_inode2(i, cr, 0) #define ufs_quota_free_inode(i, cr) ufs_quota_free_inode2(i, cr, 0) int ufs_quota_alloc_blocks2(struct inode *, int32_t, struct ucred *, enum ufs_quota_flags); int ufs_quota_free_blocks2(struct inode *, int32_t, struct ucred *, enum ufs_quota_flags); int ufs_quota_alloc_inode2(struct inode *, struct ucred *, enum ufs_quota_flags); int ufs_quota_free_inode2(struct inode *, struct ucred *, enum ufs_quota_flags); int ufs_quota_delete(struct inode *); int getinoquota(struct inode *); int quotaoff(struct proc *, struct mount *, int); int qsync(struct mount *mp); int ufs_quotactl(struct mount *, int, uid_t, caddr_t, struct proc *); void ufs_quota_init(void); __END_DECLS #endif /* _KERNEL */ #endif /* _QUOTA_ */ makefs/src/sys/ufs/ufs/ufs_bmap.c010064400000000000000000000216421021267303400142150ustar00/** $MirOS: src/sys/ufs/ufs/ufs_bmap.c,v 1.2 2005/03/06 21:28:38 tg Exp $ */ /* $OpenBSD: ufs_bmap.c,v 1.16 2003/06/02 23:28:23 millert Exp $ */ /* $NetBSD: ufs_bmap.c,v 1.3 1996/02/09 22:36:00 christos Exp $ */ /* * Copyright (c) 1989, 1991, 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. * * @(#)ufs_bmap.c 8.6 (Berkeley) 1/21/94 */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Bmap converts a the logical block number of a file to its physical block * number on the disk. The conversion is done by using the logical block * number to index into the array of block pointers described by the dinode. */ int ufs_bmap(v) void *v; { struct vop_bmap_args /* { struct vnode *a_vp; daddr_t a_bn; struct vnode **a_vpp; daddr_t *a_bnp; int *a_runp; } */ *ap = v; /* * Check for underlying vnode requests and ensure that logical * to physical mapping is requested. */ if (ap->a_vpp != NULL) *ap->a_vpp = VTOI(ap->a_vp)->i_devvp; if (ap->a_bnp == NULL) return (0); return (ufs_bmaparray(ap->a_vp, ap->a_bn, ap->a_bnp, NULL, NULL, ap->a_runp)); } /* * Indirect blocks are now on the vnode for the file. They are given negative * logical block numbers. Indirect blocks are addressed by the negative * address of the first data block to which they point. Double indirect blocks * are addressed by one less than the address of the first indirect block to * which they point. Triple indirect blocks are addressed by one less than * the address of the first double indirect block to which they point. * * ufs_bmaparray does the bmap conversion, and if requested returns the * array of logical blocks which must be traversed to get to a block. * Each entry contains the offset into that block that gets you to the * next block and the disk address of the block (if it is assigned). */ int ufs_bmaparray(vp, bn, bnp, ap, nump, runp) struct vnode *vp; daddr_t bn; daddr_t *bnp; struct indir *ap; int *nump; int *runp; { struct inode *ip; struct buf *bp; struct ufsmount *ump; struct mount *mp; struct vnode *devvp; struct indir a[NIADDR+1], *xap; daddr_t daddr; long metalbn; int error, maxrun = 0, num; ip = VTOI(vp); mp = vp->v_mount; ump = VFSTOUFS(mp); #ifdef DIAGNOSTIC if ((ap != NULL && nump == NULL) || (ap == NULL && nump != NULL)) panic("ufs_bmaparray: invalid arguments"); #endif if (runp) { /* * XXX * If MAXBSIZE is the largest transfer the disks can handle, * we probably want maxrun to be 1 block less so that we * don't create a block larger than the device can handle. */ *runp = 0; maxrun = MAXBSIZE / mp->mnt_stat.f_iosize - 1; } xap = ap == NULL ? a : ap; if (!nump) nump = # if ((error = ufs_getlbns(vp, bn, xap, nump)) != 0) return (error); num = *nump; if (num == 0) { *bnp = blkptrtodb(ump, ip->i_ffs_db[bn]); if (*bnp == 0) *bnp = -1; else if (runp) for (++bn; bn < NDADDR && *runp < maxrun && is_sequential(ump, ip->i_ffs_db[bn - 1], ip->i_ffs_db[bn]); ++bn, ++*runp); return (0); } /* Get disk address out of indirect block array */ daddr = ip->i_ffs_ib[xap->in_off]; devvp = VFSTOUFS(vp->v_mount)->um_devvp; for (bp = NULL, ++xap; --num; ++xap) { /* * Exit the loop if there is no disk address assigned yet and * the indirect block isn't in the cache, or if we were * looking for an indirect block and we've found it. */ metalbn = xap->in_lbn; if ((daddr == 0 && !incore(vp, metalbn)) || metalbn == bn) break; /* * If we get here, we've either got the block in the cache * or we have a disk address for it, go fetch it. */ if (bp) brelse(bp); xap->in_exists = 1; bp = getblk(vp, metalbn, mp->mnt_stat.f_iosize, 0, 0); if (bp->b_flags & (B_DONE | B_DELWRI)) { ; } #ifdef DIAGNOSTIC else if (!daddr) panic("ufs_bmaparray: indirect block not in cache"); #endif else { bp->b_blkno = blkptrtodb(ump, daddr); bp->b_flags |= B_READ; VOP_STRATEGY(bp); curproc->p_stats->p_ru.ru_inblock++; /* XXX */ if ((error = biowait(bp)) != 0) { brelse(bp); return (error); } } daddr = ((daddr_t *)bp->b_data)[xap->in_off]; if (num == 1 && daddr && runp) for (bn = xap->in_off + 1; bn < MNINDIR(ump) && *runp < maxrun && is_sequential(ump, ((daddr_t *)bp->b_data)[bn - 1], ((daddr_t *)bp->b_data)[bn]); ++bn, ++*runp); } if (bp) brelse(bp); daddr = blkptrtodb(ump, daddr); *bnp = daddr == 0 ? -1 : daddr; return (0); } /* * Create an array of logical block number/offset pairs which represent the * path of indirect blocks required to access a data block. The first "pair" * contains the logical block number of the appropriate single, double or * triple indirect block and the offset into the inode indirect block array. * Note, the logical block number of the inode single/double/triple indirect * block appears twice in the array, once with the offset into the i_ffs_ib and * once with the offset into the page itself. */ int ufs_getlbns(vp, bn, ap, nump) struct vnode *vp; daddr_t bn; struct indir *ap; int *nump; { long metalbn, realbn; struct ufsmount *ump; int64_t blockcnt; int i, numlevels, off; ump = VFSTOUFS(vp->v_mount); if (nump) *nump = 0; numlevels = 0; realbn = bn; if ((long)bn < 0) bn = -(long)bn; #ifdef DIAGNOSTIC if (realbn < 0 && realbn > -NDADDR) { panic ("ufs_getlbns: Invalid indirect block %ld specified", realbn); } #endif /* The first NDADDR blocks are direct blocks. */ if (bn < NDADDR) return (0); /* * Determine the number of levels of indirection. After this loop * is done, blockcnt indicates the number of data blocks possible * at the given level of indirection, and NIADDR - i is the number * of levels of indirection needed to locate the requested block. */ for (blockcnt = 1, i = NIADDR, bn -= NDADDR;; i--, bn -= blockcnt) { if (i == 0) return (EFBIG); blockcnt *= MNINDIR(ump); if (bn < blockcnt) break; } /* Calculate the address of the first meta-block. */ if (realbn >= 0) metalbn = -(realbn - bn + NIADDR - i); else metalbn = -(-realbn - bn + NIADDR - i); /* * At each iteration, off is the offset into the bap array which is * an array of disk addresses at the current level of indirection. * The logical block number and the offset in that block are stored * into the argument array. */ ap->in_lbn = metalbn; ap->in_off = off = NIADDR - i; ap->in_exists = 0; ap++; for (++numlevels; i <= NIADDR; i++) { /* If searching for a meta-data block, quit when found. */ if (metalbn == realbn) break; blockcnt /= MNINDIR(ump); off = (bn / blockcnt) % MNINDIR(ump); ++numlevels; ap->in_lbn = metalbn; ap->in_off = off; ap->in_exists = 0; ++ap; metalbn -= -1 + off * blockcnt; } #ifdef DIAGNOSTIC if (realbn < 0 && metalbn != realbn) { panic("ufs_getlbns: indirect block %ld not found", realbn); } #endif if (nump) *nump = numlevels; return (0); } makefs/src/sys/ufs/ufs/ufs_dirhash.c010064400000000000000000000725251035163125400147300ustar00/* $OpenBSD: ufs_dirhash.c,v 1.8 2004/07/21 12:10:20 art Exp $ */ /* * Copyright (c) 2001, 2002 Ian Dowse. 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. */ /* * This implements a hash-based lookup scheme for UFS directories. */ #if 0 __FBSDID("$FreeBSD: src/sys/ufs/ufs/ufs_dirhash.c,v 1.18 2004/02/15 21:39:35 dwmalone Exp $"); #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define WRAPINCR(val, limit) (((val) + 1 == (limit)) ? 0 : ((val) + 1)) #define WRAPDECR(val, limit) (((val) == 0) ? ((limit) - 1) : ((val) - 1)) #define OFSFMT(vp) ((vp)->v_mount->mnt_maxsymlinklen <= 0) #define BLKFREE2IDX(n) ((n) > DH_NFSTATS ? DH_NFSTATS : (n)) int ufs_mindirhashsize; int ufs_dirhashmaxmem; int ufs_dirhashmem; int ufs_dirhashcheck; int ufsdirhash_hash(struct dirhash *dh, char *name, int namelen); void ufsdirhash_adjfree(struct dirhash *dh, doff_t offset, int diff); void ufsdirhash_delslot(struct dirhash *dh, int slot); int ufsdirhash_findslot(struct dirhash *dh, char *name, int namelen, doff_t offset); doff_t ufsdirhash_getprev(struct direct *dp, doff_t offset); int ufsdirhash_recycle(int wanted); struct pool ufsdirhash_pool; #define DIRHASHLIST_LOCK() rw_enter_write(&ufsdirhash_mtx) #define DIRHASHLIST_UNLOCK() rw_exit_write(&ufsdirhash_mtx) #define DIRHASH_LOCK(dh) rw_enter_write(&(dh)->dh_mtx) #define DIRHASH_UNLOCK(dh) rw_exit_write(&(dh)->dh_mtx) #define DIRHASH_BLKALLOC_WAITOK() pool_get(&ufsdirhash_pool, PR_WAITOK) #define DIRHASH_BLKFREE(v) pool_put(&ufsdirhash_pool, v) #define mtx_assert(l, f) /* nothing */ #define DIRHASH_ASSERT(e, m) KASSERT((e)) /* Dirhash list; recently-used entries are near the tail. */ TAILQ_HEAD(, dirhash) ufsdirhash_list; /* Protects: ufsdirhash_list, `dh_list' field, ufs_dirhashmem. */ struct rwlock ufsdirhash_mtx; /* * Locking order: * ufsdirhash_mtx * dh_mtx * * The dh_mtx mutex should be acquired either via the inode lock, or via * ufsdirhash_mtx. Only the owner of the inode may free the associated * dirhash, but anything can steal its memory and set dh_hash to NULL. */ /* * Attempt to build up a hash table for the directory contents in * inode 'ip'. Returns 0 on success, or -1 of the operation failed. */ int ufsdirhash_build(struct inode *ip) { struct dirhash *dh; struct buf *bp = NULL; struct direct *ep; struct vnode *vp; doff_t bmask, pos; int dirblocks, i, j, memreqd, nblocks, narrays, nslots, slot; /* Check if we can/should use dirhash. */ if (ip->i_dirhash == NULL) { if (ip->i_size < ufs_mindirhashsize || OFSFMT(ip->i_vnode)) return (-1); } else { /* Hash exists, but sysctls could have changed. */ if (ip->i_size < ufs_mindirhashsize || ufs_dirhashmem > ufs_dirhashmaxmem) { ufsdirhash_free(ip); return (-1); } /* Check if hash exists and is intact (note: unlocked read). */ if (ip->i_dirhash->dh_hash != NULL) return (0); /* Free the old, recycled hash and build a new one. */ ufsdirhash_free(ip); } /* Don't hash removed directories. */ if (ip->i_effnlink == 0) return (-1); vp = ip->i_vnode; /* Allocate 50% more entries than this dir size could ever need. */ DIRHASH_ASSERT(ip->i_size >= DIRBLKSIZ, ("ufsdirhash_build size")); nslots = ip->i_size / DIRECTSIZ(1); nslots = (nslots * 3 + 1) / 2; narrays = howmany(nslots, DH_NBLKOFF); nslots = narrays * DH_NBLKOFF; dirblocks = howmany(ip->i_size, DIRBLKSIZ); nblocks = (dirblocks * 3 + 1) / 2; memreqd = sizeof(*dh) + narrays * sizeof(*dh->dh_hash) + narrays * DH_NBLKOFF * sizeof(**dh->dh_hash) + nblocks * sizeof(*dh->dh_blkfree); DIRHASHLIST_LOCK(); if (memreqd + ufs_dirhashmem > ufs_dirhashmaxmem) { DIRHASHLIST_UNLOCK(); if (memreqd > ufs_dirhashmaxmem / 2) return (-1); /* Try to free some space. */ if (ufsdirhash_recycle(memreqd) != 0) return (-1); /* Enough was freed, and list has been locked. */ } ufs_dirhashmem += memreqd; DIRHASHLIST_UNLOCK(); /* * Use non-blocking mallocs so that we will revert to a linear * lookup on failure rather than potentially blocking forever. */ MALLOC(dh, struct dirhash *, sizeof *dh, M_DIRHASH, M_NOWAIT); if (dh == NULL) { DIRHASHLIST_LOCK(); ufs_dirhashmem -= memreqd; DIRHASHLIST_UNLOCK(); return (-1); } memset(dh, 0, sizeof *dh); dh->dh_hash = malloc(narrays * sizeof(dh->dh_hash[0]), M_DIRHASH, M_NOWAIT); memset(dh->dh_hash, 0, narrays * sizeof(dh->dh_hash[0])); dh->dh_blkfree = malloc(nblocks * sizeof(dh->dh_blkfree[0]), M_DIRHASH, M_NOWAIT); if (dh->dh_hash == NULL || dh->dh_blkfree == NULL) goto fail; for (i = 0; i < narrays; i++) { if ((dh->dh_hash[i] = DIRHASH_BLKALLOC_WAITOK()) == NULL) goto fail; for (j = 0; j < DH_NBLKOFF; j++) dh->dh_hash[i][j] = DIRHASH_EMPTY; } /* Initialise the hash table and block statistics. */ rw_init(&dh->dh_mtx); dh->dh_narrays = narrays; dh->dh_hlen = nslots; dh->dh_nblk = nblocks; dh->dh_dirblks = dirblocks; for (i = 0; i < dirblocks; i++) dh->dh_blkfree[i] = DIRBLKSIZ / DIRALIGN; for (i = 0; i < DH_NFSTATS; i++) dh->dh_firstfree[i] = -1; dh->dh_firstfree[DH_NFSTATS] = 0; dh->dh_seqopt = 0; dh->dh_seqoff = 0; dh->dh_score = DH_SCOREINIT; ip->i_dirhash = dh; bmask = VFSTOUFS(vp->v_mount)->um_mountp->mnt_stat.f_iosize - 1; pos = 0; while (pos < ip->i_size) { /* If necessary, get the next directory block. */ if ((pos & bmask) == 0) { if (bp != NULL) brelse(bp); if (UFS_BUFATOFF(ip, (off_t)pos, NULL, &bp) != 0) goto fail; } /* Add this entry to the hash. */ ep = (struct direct *)((char *)bp->b_data + (pos & bmask)); if (ep->d_reclen == 0 || ep->d_reclen > DIRBLKSIZ - (pos & (DIRBLKSIZ - 1))) { /* Corrupted directory. */ brelse(bp); goto fail; } if (ep->d_ino != 0) { /* Add the entry (simplified ufsdirhash_add). */ slot = ufsdirhash_hash(dh, ep->d_name, ep->d_namlen); while (DH_ENTRY(dh, slot) != DIRHASH_EMPTY) slot = WRAPINCR(slot, dh->dh_hlen); dh->dh_hused++; DH_ENTRY(dh, slot) = pos; ufsdirhash_adjfree(dh, pos, -DIRSIZ(0, ep)); } pos += ep->d_reclen; } if (bp != NULL) brelse(bp); DIRHASHLIST_LOCK(); TAILQ_INSERT_TAIL(&ufsdirhash_list, dh, dh_list); dh->dh_onlist = 1; DIRHASHLIST_UNLOCK(); return (0); fail: if (dh->dh_hash != NULL) { for (i = 0; i < narrays; i++) if (dh->dh_hash[i] != NULL) DIRHASH_BLKFREE(dh->dh_hash[i]); free(dh->dh_hash, M_DIRHASH); } if (dh->dh_blkfree != NULL) free(dh->dh_blkfree, M_DIRHASH); FREE(dh, M_DIRHASH); ip->i_dirhash = NULL; DIRHASHLIST_LOCK(); ufs_dirhashmem -= memreqd; DIRHASHLIST_UNLOCK(); return (-1); } /* * Free any hash table associated with inode 'ip'. */ void ufsdirhash_free(struct inode *ip) { struct dirhash *dh; int i, mem; if ((dh = ip->i_dirhash) == NULL) return; DIRHASHLIST_LOCK(); DIRHASH_LOCK(dh); if (dh->dh_onlist) TAILQ_REMOVE(&ufsdirhash_list, dh, dh_list); DIRHASH_UNLOCK(dh); DIRHASHLIST_UNLOCK(); /* The dirhash pointed to by 'dh' is exclusively ours now. */ mem = sizeof(*dh); if (dh->dh_hash != NULL) { for (i = 0; i < dh->dh_narrays; i++) DIRHASH_BLKFREE(dh->dh_hash[i]); free(dh->dh_hash, M_DIRHASH); free(dh->dh_blkfree, M_DIRHASH); mem += dh->dh_narrays * sizeof(*dh->dh_hash) + dh->dh_narrays * DH_NBLKOFF * sizeof(**dh->dh_hash) + dh->dh_nblk * sizeof(*dh->dh_blkfree); } FREE(dh, M_DIRHASH); ip->i_dirhash = NULL; DIRHASHLIST_LOCK(); ufs_dirhashmem -= mem; DIRHASHLIST_UNLOCK(); } /* * Find the offset of the specified name within the given inode. * Returns 0 on success, ENOENT if the entry does not exist, or * EJUSTRETURN if the caller should revert to a linear search. * * If successful, the directory offset is stored in *offp, and a * pointer to a struct buf containing the entry is stored in *bpp. If * prevoffp is non-NULL, the offset of the previous entry within * the DIRBLKSIZ-sized block is stored in *prevoffp (if the entry * is the first in a block, the start of the block is used). */ int ufsdirhash_lookup(struct inode *ip, char *name, int namelen, doff_t *offp, struct buf **bpp, doff_t *prevoffp) { struct dirhash *dh, *dh_next; struct direct *dp; struct vnode *vp; struct buf *bp; doff_t blkoff, bmask, offset, prevoff; int i, slot; if ((dh = ip->i_dirhash) == NULL) return (EJUSTRETURN); /* * Move this dirhash towards the end of the list if it has a * score higher than the next entry, and acquire the dh_mtx. * Optimise the case where it's already the last by performing * an unlocked read of the TAILQ_NEXT pointer. * * In both cases, end up holding just dh_mtx. */ if (TAILQ_NEXT(dh, dh_list) != NULL) { DIRHASHLIST_LOCK(); DIRHASH_LOCK(dh); /* * If the new score will be greater than that of the next * entry, then move this entry past it. With both mutexes * held, dh_next won't go away, but its dh_score could * change; that's not important since it is just a hint. */ if (dh->dh_hash != NULL && (dh_next = TAILQ_NEXT(dh, dh_list)) != NULL && dh->dh_score >= dh_next->dh_score) { DIRHASH_ASSERT(dh->dh_onlist, ("dirhash: not on list")); TAILQ_REMOVE(&ufsdirhash_list, dh, dh_list); TAILQ_INSERT_AFTER(&ufsdirhash_list, dh_next, dh, dh_list); } DIRHASHLIST_UNLOCK(); } else { /* Already the last, though that could change as we wait. */ DIRHASH_LOCK(dh); } if (dh->dh_hash == NULL) { DIRHASH_UNLOCK(dh); ufsdirhash_free(ip); return (EJUSTRETURN); } /* Update the score. */ if (dh->dh_score < DH_SCOREMAX) dh->dh_score++; vp = ip->i_vnode; bmask = VFSTOUFS(vp->v_mount)->um_mountp->mnt_stat.f_iosize - 1; blkoff = -1; bp = NULL; restart: slot = ufsdirhash_hash(dh, name, namelen); if (dh->dh_seqopt) { /* * Sequential access optimisation. dh_seqoff contains the * offset of the directory entry immediately following * the last entry that was looked up. Check if this offset * appears in the hash chain for the name we are looking for. */ for (i = slot; (offset = DH_ENTRY(dh, i)) != DIRHASH_EMPTY; i = WRAPINCR(i, dh->dh_hlen)) if (offset == dh->dh_seqoff) break; if (offset == dh->dh_seqoff) { /* * We found an entry with the expected offset. This * is probably the entry we want, but if not, the * code below will turn off seqoff and retry. */ slot = i; } else dh->dh_seqopt = 0; } for (; (offset = DH_ENTRY(dh, slot)) != DIRHASH_EMPTY; slot = WRAPINCR(slot, dh->dh_hlen)) { if (offset == DIRHASH_DEL) continue; DIRHASH_UNLOCK(dh); if (offset < 0 || offset >= ip->i_size) panic("ufsdirhash_lookup: bad offset in hash array"); if ((offset & ~bmask) != blkoff) { if (bp != NULL) brelse(bp); blkoff = offset & ~bmask; if (UFS_BUFATOFF(ip, (off_t)blkoff, NULL, &bp) != 0) return (EJUSTRETURN); } dp = (struct direct *)(bp->b_data + (offset & bmask)); if (dp->d_reclen == 0 || dp->d_reclen > DIRBLKSIZ - (offset & (DIRBLKSIZ - 1))) { /* Corrupted directory. */ brelse(bp); return (EJUSTRETURN); } if (dp->d_namlen == namelen && bcmp(dp->d_name, name, namelen) == 0) { /* Found. Get the prev offset if needed. */ if (prevoffp != NULL) { if (offset & (DIRBLKSIZ - 1)) { prevoff = ufsdirhash_getprev(dp, offset); if (prevoff == -1) { brelse(bp); return (EJUSTRETURN); } } else prevoff = offset; *prevoffp = prevoff; } /* Check for sequential access, and update offset. */ if (dh->dh_seqopt == 0 && dh->dh_seqoff == offset) dh->dh_seqopt = 1; dh->dh_seqoff = offset + DIRSIZ(0, dp); *bpp = bp; *offp = offset; return (0); } DIRHASH_LOCK(dh); if (dh->dh_hash == NULL) { DIRHASH_UNLOCK(dh); if (bp != NULL) brelse(bp); ufsdirhash_free(ip); return (EJUSTRETURN); } /* * When the name doesn't match in the seqopt case, go back * and search normally. */ if (dh->dh_seqopt) { dh->dh_seqopt = 0; goto restart; } } DIRHASH_UNLOCK(dh); if (bp != NULL) brelse(bp); return (ENOENT); } /* * Find a directory block with room for 'slotneeded' bytes. Returns * the offset of the directory entry that begins the free space. * This will either be the offset of an existing entry that has free * space at the end, or the offset of an entry with d_ino == 0 at * the start of a DIRBLKSIZ block. * * To use the space, the caller may need to compact existing entries in * the directory. The total number of bytes in all of the entries involved * in the compaction is stored in *slotsize. In other words, all of * the entries that must be compacted are exactly contained in the * region beginning at the returned offset and spanning *slotsize bytes. * * Returns -1 if no space was found, indicating that the directory * must be extended. */ doff_t ufsdirhash_findfree(struct inode *ip, int slotneeded, int *slotsize) { struct direct *dp; struct dirhash *dh; struct buf *bp; doff_t pos, slotstart; int dirblock, error, freebytes, i; if ((dh = ip->i_dirhash) == NULL) return (-1); DIRHASH_LOCK(dh); if (dh->dh_hash == NULL) { DIRHASH_UNLOCK(dh); ufsdirhash_free(ip); return (-1); } /* Find a directory block with the desired free space. */ dirblock = -1; for (i = howmany(slotneeded, DIRALIGN); i <= DH_NFSTATS; i++) if ((dirblock = dh->dh_firstfree[i]) != -1) break; if (dirblock == -1) { DIRHASH_UNLOCK(dh); return (-1); } DIRHASH_ASSERT(dirblock < dh->dh_nblk && dh->dh_blkfree[dirblock] >= howmany(slotneeded, DIRALIGN), ("ufsdirhash_findfree: bad stats")); DIRHASH_UNLOCK(dh); pos = dirblock * DIRBLKSIZ; error = UFS_BUFATOFF(ip, (off_t)pos, (char **)&dp, &bp); if (error) return (-1); /* Find the first entry with free space. */ for (i = 0; i < DIRBLKSIZ; ) { if (dp->d_reclen == 0) { brelse(bp); return (-1); } if (dp->d_ino == 0 || dp->d_reclen > DIRSIZ(0, dp)) break; i += dp->d_reclen; dp = (struct direct *)((char *)dp + dp->d_reclen); } if (i > DIRBLKSIZ) { brelse(bp); return (-1); } slotstart = pos + i; /* Find the range of entries needed to get enough space */ freebytes = 0; while (i < DIRBLKSIZ && freebytes < slotneeded) { freebytes += dp->d_reclen; if (dp->d_ino != 0) freebytes -= DIRSIZ(0, dp); if (dp->d_reclen == 0) { brelse(bp); return (-1); } i += dp->d_reclen; dp = (struct direct *)((char *)dp + dp->d_reclen); } if (i > DIRBLKSIZ) { brelse(bp); return (-1); } if (freebytes < slotneeded) panic("ufsdirhash_findfree: free mismatch"); brelse(bp); *slotsize = pos + i - slotstart; return (slotstart); } /* * Return the start of the unused space at the end of a directory, or * -1 if there are no trailing unused blocks. */ doff_t ufsdirhash_enduseful(struct inode *ip) { struct dirhash *dh; int i; if ((dh = ip->i_dirhash) == NULL) return (-1); DIRHASH_LOCK(dh); if (dh->dh_hash == NULL) { DIRHASH_UNLOCK(dh); ufsdirhash_free(ip); return (-1); } if (dh->dh_blkfree[dh->dh_dirblks - 1] != DIRBLKSIZ / DIRALIGN) { DIRHASH_UNLOCK(dh); return (-1); } for (i = dh->dh_dirblks - 1; i >= 0; i--) if (dh->dh_blkfree[i] != DIRBLKSIZ / DIRALIGN) break; DIRHASH_UNLOCK(dh); return ((doff_t)(i + 1) * DIRBLKSIZ); } /* * Insert information into the hash about a new directory entry. dirp * points to a struct direct containing the entry, and offset specifies * the offset of this entry. */ void ufsdirhash_add(struct inode *ip, struct direct *dirp, doff_t offset) { struct dirhash *dh; int slot; if ((dh = ip->i_dirhash) == NULL) return; DIRHASH_LOCK(dh); if (dh->dh_hash == NULL) { DIRHASH_UNLOCK(dh); ufsdirhash_free(ip); return; } DIRHASH_ASSERT(offset < dh->dh_dirblks * DIRBLKSIZ, ("ufsdirhash_add: bad offset")); /* * Normal hash usage is < 66%. If the usage gets too high then * remove the hash entirely and let it be rebuilt later. */ if (dh->dh_hused >= (dh->dh_hlen * 3) / 4) { DIRHASH_UNLOCK(dh); ufsdirhash_free(ip); return; } /* Find a free hash slot (empty or deleted), and add the entry. */ slot = ufsdirhash_hash(dh, dirp->d_name, dirp->d_namlen); while (DH_ENTRY(dh, slot) >= 0) slot = WRAPINCR(slot, dh->dh_hlen); if (DH_ENTRY(dh, slot) == DIRHASH_EMPTY) dh->dh_hused++; DH_ENTRY(dh, slot) = offset; /* Update the per-block summary info. */ ufsdirhash_adjfree(dh, offset, -DIRSIZ(0, dirp)); DIRHASH_UNLOCK(dh); } /* * Remove the specified directory entry from the hash. The entry to remove * is defined by the name in `dirp', which must exist at the specified * `offset' within the directory. */ void ufsdirhash_remove(struct inode *ip, struct direct *dirp, doff_t offset) { struct dirhash *dh; int slot; if ((dh = ip->i_dirhash) == NULL) return; DIRHASH_LOCK(dh); if (dh->dh_hash == NULL) { DIRHASH_UNLOCK(dh); ufsdirhash_free(ip); return; } DIRHASH_ASSERT(offset < dh->dh_dirblks * DIRBLKSIZ, ("ufsdirhash_remove: bad offset")); /* Find the entry */ slot = ufsdirhash_findslot(dh, dirp->d_name, dirp->d_namlen, offset); /* Remove the hash entry. */ ufsdirhash_delslot(dh, slot); /* Update the per-block summary info. */ ufsdirhash_adjfree(dh, offset, DIRSIZ(0, dirp)); DIRHASH_UNLOCK(dh); } /* * Change the offset associated with a directory entry in the hash. Used * when compacting directory blocks. */ void ufsdirhash_move(struct inode *ip, struct direct *dirp, doff_t oldoff, doff_t newoff) { struct dirhash *dh; int slot; if ((dh = ip->i_dirhash) == NULL) return; DIRHASH_LOCK(dh); if (dh->dh_hash == NULL) { DIRHASH_UNLOCK(dh); ufsdirhash_free(ip); return; } DIRHASH_ASSERT(oldoff < dh->dh_dirblks * DIRBLKSIZ && newoff < dh->dh_dirblks * DIRBLKSIZ, ("ufsdirhash_move: bad offset")); /* Find the entry, and update the offset. */ slot = ufsdirhash_findslot(dh, dirp->d_name, dirp->d_namlen, oldoff); DH_ENTRY(dh, slot) = newoff; DIRHASH_UNLOCK(dh); } /* * Inform dirhash that the directory has grown by one block that * begins at offset (i.e. the new length is offset + DIRBLKSIZ). */ void ufsdirhash_newblk(struct inode *ip, doff_t offset) { struct dirhash *dh; int block; if ((dh = ip->i_dirhash) == NULL) return; DIRHASH_LOCK(dh); if (dh->dh_hash == NULL) { DIRHASH_UNLOCK(dh); ufsdirhash_free(ip); return; } DIRHASH_ASSERT(offset == dh->dh_dirblks * DIRBLKSIZ, ("ufsdirhash_newblk: bad offset")); block = offset / DIRBLKSIZ; if (block >= dh->dh_nblk) { /* Out of space; must rebuild. */ DIRHASH_UNLOCK(dh); ufsdirhash_free(ip); return; } dh->dh_dirblks = block + 1; /* Account for the new free block. */ dh->dh_blkfree[block] = DIRBLKSIZ / DIRALIGN; if (dh->dh_firstfree[DH_NFSTATS] == -1) dh->dh_firstfree[DH_NFSTATS] = block; DIRHASH_UNLOCK(dh); } /* * Inform dirhash that the directory is being truncated. */ void ufsdirhash_dirtrunc(struct inode *ip, doff_t offset) { struct dirhash *dh; int block, i; if ((dh = ip->i_dirhash) == NULL) return; DIRHASH_LOCK(dh); if (dh->dh_hash == NULL) { DIRHASH_UNLOCK(dh); ufsdirhash_free(ip); return; } DIRHASH_ASSERT(offset <= dh->dh_dirblks * DIRBLKSIZ, ("ufsdirhash_dirtrunc: bad offset")); block = howmany(offset, DIRBLKSIZ); /* * If the directory shrinks to less than 1/8 of dh_nblk blocks * (about 20% of its original size due to the 50% extra added in * ufsdirhash_build) then free it, and let the caller rebuild * if necessary. */ if (block < dh->dh_nblk / 8 && dh->dh_narrays > 1) { DIRHASH_UNLOCK(dh); ufsdirhash_free(ip); return; } /* * Remove any `first free' information pertaining to the * truncated blocks. All blocks we're removing should be * completely unused. */ if (dh->dh_firstfree[DH_NFSTATS] >= block) dh->dh_firstfree[DH_NFSTATS] = -1; for (i = block; i < dh->dh_dirblks; i++) if (dh->dh_blkfree[i] != DIRBLKSIZ / DIRALIGN) panic("ufsdirhash_dirtrunc: blocks in use"); for (i = 0; i < DH_NFSTATS; i++) if (dh->dh_firstfree[i] >= block) panic("ufsdirhash_dirtrunc: first free corrupt"); dh->dh_dirblks = block; DIRHASH_UNLOCK(dh); } /* * Debugging function to check that the dirhash information about * a directory block matches its actual contents. Panics if a mismatch * is detected. * * On entry, `buf' should point to the start of an in-core * DIRBLKSIZ-sized directory block, and `offset' should contain the * offset from the start of the directory of that block. */ void ufsdirhash_checkblock(struct inode *ip, char *buf, doff_t offset) { struct dirhash *dh; struct direct *dp; int block, ffslot, i, nfree; if (!ufs_dirhashcheck) return; if ((dh = ip->i_dirhash) == NULL) return; DIRHASH_LOCK(dh); if (dh->dh_hash == NULL) { DIRHASH_UNLOCK(dh); ufsdirhash_free(ip); return; } block = offset / DIRBLKSIZ; if ((offset & (DIRBLKSIZ - 1)) != 0 || block >= dh->dh_dirblks) panic("ufsdirhash_checkblock: bad offset"); nfree = 0; for (i = 0; i < DIRBLKSIZ; i += dp->d_reclen) { dp = (struct direct *)(buf + i); if (dp->d_reclen == 0 || i + dp->d_reclen > DIRBLKSIZ) panic("ufsdirhash_checkblock: bad dir"); if (dp->d_ino == 0) { #if 0 /* * XXX entries with d_ino == 0 should only occur * at the start of a DIRBLKSIZ block. However the * ufs code is tolerant of such entries at other * offsets, and fsck does not fix them. */ if (i != 0) panic("ufsdirhash_checkblock: bad dir inode"); #endif nfree += dp->d_reclen; continue; } /* Check that the entry exists (will panic if it doesn't). */ ufsdirhash_findslot(dh, dp->d_name, dp->d_namlen, offset + i); nfree += dp->d_reclen - DIRSIZ(0, dp); } if (i != DIRBLKSIZ) panic("ufsdirhash_checkblock: bad dir end"); if (dh->dh_blkfree[block] * DIRALIGN != nfree) panic("ufsdirhash_checkblock: bad free count"); ffslot = BLKFREE2IDX(nfree / DIRALIGN); for (i = 0; i <= DH_NFSTATS; i++) if (dh->dh_firstfree[i] == block && i != ffslot) panic("ufsdirhash_checkblock: bad first-free"); if (dh->dh_firstfree[ffslot] == -1) panic("ufsdirhash_checkblock: missing first-free entry"); DIRHASH_UNLOCK(dh); } /* * Hash the specified filename into a dirhash slot. */ int ufsdirhash_hash(struct dirhash *dh, char *name, int namelen) { u_int32_t hash; /* * We hash the name and then some other bit of data that is * invariant over the dirhash's lifetime. Otherwise names * differing only in the last byte are placed close to one * another in the table, which is bad for linear probing. */ hash = hash32_buf(name, namelen, HASHINIT); hash = hash32_buf(&dh, sizeof(dh), hash); return (hash % dh->dh_hlen); } /* * Adjust the number of free bytes in the block containing `offset' * by the value specified by `diff'. * * The caller must ensure we have exclusive access to `dh'; normally * that means that dh_mtx should be held, but this is also called * from ufsdirhash_build() where exclusive access can be assumed. */ void ufsdirhash_adjfree(struct dirhash *dh, doff_t offset, int diff) { int block, i, nfidx, ofidx; /* Update the per-block summary info. */ block = offset / DIRBLKSIZ; DIRHASH_ASSERT(block < dh->dh_nblk && block < dh->dh_dirblks, ("dirhash bad offset")); ofidx = BLKFREE2IDX(dh->dh_blkfree[block]); dh->dh_blkfree[block] = (int)dh->dh_blkfree[block] + (diff / DIRALIGN); nfidx = BLKFREE2IDX(dh->dh_blkfree[block]); /* Update the `first free' list if necessary. */ if (ofidx != nfidx) { /* If removing, scan forward for the next block. */ if (dh->dh_firstfree[ofidx] == block) { for (i = block + 1; i < dh->dh_dirblks; i++) if (BLKFREE2IDX(dh->dh_blkfree[i]) == ofidx) break; dh->dh_firstfree[ofidx] = (i < dh->dh_dirblks) ? i : -1; } /* Make this the new `first free' if necessary */ if (dh->dh_firstfree[nfidx] > block || dh->dh_firstfree[nfidx] == -1) dh->dh_firstfree[nfidx] = block; } } /* * Find the specified name which should have the specified offset. * Returns a slot number, and panics on failure. * * `dh' must be locked on entry and remains so on return. */ int ufsdirhash_findslot(struct dirhash *dh, char *name, int namelen, doff_t offset) { int slot; mtx_assert(&dh->dh_mtx, MA_OWNED); /* Find the entry. */ DIRHASH_ASSERT(dh->dh_hused < dh->dh_hlen, ("dirhash find full")); slot = ufsdirhash_hash(dh, name, namelen); while (DH_ENTRY(dh, slot) != offset && DH_ENTRY(dh, slot) != DIRHASH_EMPTY) slot = WRAPINCR(slot, dh->dh_hlen); if (DH_ENTRY(dh, slot) != offset) panic("ufsdirhash_findslot: '%.*s' not found", namelen, name); return (slot); } /* * Remove the entry corresponding to the specified slot from the hash array. * * `dh' must be locked on entry and remains so on return. */ void ufsdirhash_delslot(struct dirhash *dh, int slot) { int i; mtx_assert(&dh->dh_mtx, MA_OWNED); /* Mark the entry as deleted. */ DH_ENTRY(dh, slot) = DIRHASH_DEL; /* If this is the end of a chain of DIRHASH_DEL slots, remove them. */ for (i = slot; DH_ENTRY(dh, i) == DIRHASH_DEL; ) i = WRAPINCR(i, dh->dh_hlen); if (DH_ENTRY(dh, i) == DIRHASH_EMPTY) { i = WRAPDECR(i, dh->dh_hlen); while (DH_ENTRY(dh, i) == DIRHASH_DEL) { DH_ENTRY(dh, i) = DIRHASH_EMPTY; dh->dh_hused--; i = WRAPDECR(i, dh->dh_hlen); } DIRHASH_ASSERT(dh->dh_hused >= 0, ("ufsdirhash_delslot neg hlen")); } } /* * Given a directory entry and its offset, find the offset of the * previous entry in the same DIRBLKSIZ-sized block. Returns an * offset, or -1 if there is no previous entry in the block or some * other problem occurred. */ doff_t ufsdirhash_getprev(struct direct *dirp, doff_t offset) { struct direct *dp; char *blkbuf; doff_t blkoff, prevoff; int entrypos, i; blkoff = offset & ~(DIRBLKSIZ - 1); /* offset of start of block */ entrypos = offset & (DIRBLKSIZ - 1); /* entry relative to block */ blkbuf = (char *)dirp - entrypos; prevoff = blkoff; /* If `offset' is the start of a block, there is no previous entry. */ if (entrypos == 0) return (-1); /* Scan from the start of the block until we get to the entry. */ for (i = 0; i < entrypos; i += dp->d_reclen) { dp = (struct direct *)(blkbuf + i); if (dp->d_reclen == 0 || i + dp->d_reclen > entrypos) return (-1); /* Corrupted directory. */ prevoff = blkoff + i; } return (prevoff); } /* * Try to free up `wanted' bytes by stealing memory from existing * dirhashes. Returns zero with list locked if successful. */ int ufsdirhash_recycle(int wanted) { struct dirhash *dh; doff_t **hash; u_int8_t *blkfree; int i, mem, narrays; DIRHASHLIST_LOCK(); while (wanted + ufs_dirhashmem > ufs_dirhashmaxmem) { /* Find a dirhash, and lock it. */ if ((dh = TAILQ_FIRST(&ufsdirhash_list)) == NULL) { DIRHASHLIST_UNLOCK(); return (-1); } DIRHASH_LOCK(dh); DIRHASH_ASSERT(dh->dh_hash != NULL, ("dirhash: NULL hash on list")); /* Decrement the score; only recycle if it becomes zero. */ if (--dh->dh_score > 0) { DIRHASH_UNLOCK(dh); DIRHASHLIST_UNLOCK(); return (-1); } /* Remove it from the list and detach its memory. */ TAILQ_REMOVE(&ufsdirhash_list, dh, dh_list); dh->dh_onlist = 0; hash = dh->dh_hash; dh->dh_hash = NULL; blkfree = dh->dh_blkfree; dh->dh_blkfree = NULL; narrays = dh->dh_narrays; mem = narrays * sizeof(*dh->dh_hash) + narrays * DH_NBLKOFF * sizeof(**dh->dh_hash) + dh->dh_nblk * sizeof(*dh->dh_blkfree); /* Unlock everything, free the detached memory. */ DIRHASH_UNLOCK(dh); DIRHASHLIST_UNLOCK(); for (i = 0; i < narrays; i++) DIRHASH_BLKFREE(hash[i]); free(hash, M_DIRHASH); free(blkfree, M_DIRHASH); /* Account for the returned memory, and repeat if necessary. */ DIRHASHLIST_LOCK(); ufs_dirhashmem -= mem; } /* Success; return with list locked. */ return (0); } void ufsdirhash_init() { pool_init(&ufsdirhash_pool, DH_NBLKOFF * sizeof(doff_t), 0, 0, 0, "dirhash", &pool_allocator_nointr); rw_init(&ufsdirhash_mtx); TAILQ_INIT(&ufsdirhash_list); #if defined (__sparc__) if (!CPU_ISSUN4OR4C) #elif defined (__vax__) if (0) #endif ufs_dirhashmaxmem = 2 * 1024 * 1024; ufs_mindirhashsize = 5 * DIRBLKSIZ; } void ufsdirhash_uninit() { DIRHASH_ASSERT(TAILQ_EMPTY(&ufsdirhash_list), ("ufsdirhash_uninit")); pool_destroy(&ufsdirhash_pool); } makefs/src/sys/ufs/ufs/ufs_extern.h010064400000000000000000000123611027001310400145740ustar00/* $OpenBSD: ufs_extern.h,v 1.25 2005/07/20 16:30:35 pedro Exp $ */ /* $NetBSD: ufs_extern.h,v 1.5 1996/02/09 22:36:03 christos Exp $ */ /*- * Copyright (c) 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. * * @(#)ufs_extern.h 8.6 (Berkeley) 8/10/94 */ struct buf; struct componentname; struct direct; struct disklabel; struct dquot; struct fid; struct flock; struct indir; struct inode; struct mbuf; struct mount; struct nameidata; struct proc; struct ucred; struct ufs_args; struct ufsmount; struct uio; struct vattr; struct vfsconf; struct vnode; __BEGIN_DECLS int ufs_access(void *); int ufs_advlock(void *); int ufs_bmap(void *); int ufs_close(void *); int ufs_create(void *); int ufs_getattr(void *); int ufs_inactive(void *); int ufs_ioctl(void *); int ufs_islocked(void *); #ifdef NFSSERVER int lease_check(void *); #define ufs_lease_check lease_check #else #define ufs_lease_check ((int (*)(void *))nullop) #endif int ufs_link(void *); int ufs_lock(void *); int ufs_lookup(void *); int ufs_mkdir(void *); int ufs_mknod(void *); int ufs_mmap(void *); int ufs_open(void *); int ufs_pathconf(void *); int ufs_print(void *); int ufs_readdir(void *); int ufs_readlink(void *); int ufs_remove(void *); int ufs_rename(void *); #define ufs_revoke vop_generic_revoke int ufs_rmdir(void *); int ufs_seek(void *); int ufs_poll(void *); int ufs_kqfilter(void *); int ufs_setattr(void *); int ufs_strategy(void *); int ufs_symlink(void *); int ufs_unlock(void *); int ufsspec_close(void *); int ufsspec_read(void *); int ufsspec_write(void *); #ifdef FIFO int ufsfifo_read(void *); int ufsfifo_write(void *); int ufsfifo_close(void *); #endif /* ufs_bmap.c */ int ufs_bmaparray(struct vnode *, daddr_t, daddr_t *, struct indir *, int *, int *); int ufs_getlbns(struct vnode *, daddr_t, struct indir *, int *); /* ufs_ihash.c */ void ufs_ihashinit(void); struct vnode *ufs_ihashlookup(dev_t, ino_t); struct vnode *ufs_ihashget(dev_t, ino_t); int ufs_ihashins(struct inode *); void ufs_ihashrem(struct inode *); /* ufs_inode.c */ int ufs_init(struct vfsconf *); int ufs_reclaim(struct vnode *, struct proc *); /* ufs_lookup.c */ void ufs_dirbad(struct inode *, doff_t, char *); int ufs_dirbadentry(struct vnode *, struct direct *, int); void ufs_makedirentry(struct inode *, struct componentname *, struct direct *); int ufs_direnter(struct vnode *, struct vnode *, struct direct *, struct componentname *, struct buf *); int ufs_dirremove(struct vnode *, struct inode *, int, int); int ufs_dirrewrite(struct inode *, struct inode *, ino_t, int, int); int ufs_dirempty(struct inode *, ino_t, struct ucred *); int ufs_checkpath(struct inode *, struct inode *, struct ucred *); /* ufs_vfsops.c */ int ufs_start(struct mount *, int, struct proc *); int ufs_root(struct mount *, struct vnode **); int ufs_quotactl(struct mount *, int, uid_t, caddr_t, struct proc *); int ufs_fhtovp(struct mount *, struct ufid *, struct vnode **); int ufs_check_export(struct mount *, struct mbuf *, int *, struct ucred **); /* ufs_vnops.c */ int ufs_vinit(struct mount *, int (**)(void *), int (**)(void *), struct vnode **); int ufs_makeinode(int, struct vnode *, struct vnode **, struct componentname *); /* * Soft dependency function prototypes. */ int softdep_setup_directory_add(struct buf *, struct inode *, off_t, long, struct buf *, int); void softdep_change_directoryentry_offset(struct inode *, caddr_t, caddr_t, caddr_t, int); void softdep_setup_remove(struct buf *,struct inode *, struct inode *, int); void softdep_setup_directory_change(struct buf *, struct inode *, struct inode *, long, int); void softdep_change_linkcnt(struct inode *, int); int softdep_slowdown(struct vnode *); __END_DECLS makefs/src/sys/ufs/ufs/ufs_ihash.c010064400000000000000000000107451026206255500144010ustar00/* $OpenBSD: ufs_ihash.c,v 1.10 2004/12/26 21:22:14 miod Exp $ */ /* $NetBSD: ufs_ihash.c,v 1.3 1996/02/09 22:36:04 christos Exp $ */ /* * Copyright (c) 1982, 1986, 1989, 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. * * @(#)ufs_ihash.c 8.4 (Berkeley) 12/30/93 */ #include #include #include #include #include #include #include #include /* * Structures associated with inode cacheing. */ LIST_HEAD(ihashhead, inode) *ihashtbl; u_long ihash; /* size of hash table - 1 */ #define INOHASH(device, inum) (&ihashtbl[((device) + (inum)) & ihash]) struct simplelock ufs_ihash_slock; /* * Initialize inode hash table. */ void ufs_ihashinit() { ihashtbl = hashinit(desiredvnodes, M_UFSMNT, M_WAITOK, &ihash); simple_lock_init(&ufs_ihash_slock); } /* * Use the device/inum pair to find the incore inode, and return a pointer * to it. If it is in core, return it, even if it is locked. */ struct vnode * ufs_ihashlookup(dev, inum) dev_t dev; ino_t inum; { struct inode *ip; simple_lock(&ufs_ihash_slock); LIST_FOREACH(ip, INOHASH(dev, inum), i_hash) if (inum == ip->i_number && dev == ip->i_dev) break; simple_unlock(&ufs_ihash_slock); if (ip) return (ITOV(ip)); return (NULLVP); } /* * Use the device/inum pair to find the incore inode, and return a pointer * to it. If it is in core, but locked, wait for it. */ struct vnode * ufs_ihashget(dev, inum) dev_t dev; ino_t inum; { struct proc *p = curproc; struct inode *ip; struct vnode *vp; loop: simple_lock(&ufs_ihash_slock); LIST_FOREACH(ip, INOHASH(dev, inum), i_hash) { if (inum == ip->i_number && dev == ip->i_dev) { vp = ITOV(ip); simple_lock(&vp->v_interlock); simple_unlock(&ufs_ihash_slock); if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, p)) goto loop; return (vp); } } simple_unlock(&ufs_ihash_slock); return (NULL); } /* * Insert the inode into the hash table, and return it locked. */ int ufs_ihashins(ip) struct inode *ip; { struct inode *curip; struct proc *p = curproc; /* XXX */ struct ihashhead *ipp; dev_t dev = ip->i_dev; ino_t inum = ip->i_number; /* lock the inode, then put it on the appropriate hash list */ lockmgr(&ip->i_lock, LK_EXCLUSIVE, (struct simplelock *)0, p); simple_lock(&ufs_ihash_slock); LIST_FOREACH(curip, INOHASH(dev, inum), i_hash) { if (inum == curip->i_number && dev == curip->i_dev) { simple_unlock(&ufs_ihash_slock); lockmgr(&ip->i_lock, LK_RELEASE, (struct simplelock *)0, p); return (EEXIST); } } ipp = INOHASH(dev, inum); LIST_INSERT_HEAD(ipp, ip, i_hash); simple_unlock(&ufs_ihash_slock); return (0); } /* * Remove the inode from the hash table. */ void ufs_ihashrem(ip) struct inode *ip; { simple_lock(&ufs_ihash_slock); if (ip->i_hash.le_prev == NULL) return; LIST_REMOVE(ip, i_hash); #ifdef DIAGNOSTIC ip->i_hash.le_next = NULL; ip->i_hash.le_prev = NULL; #endif simple_unlock(&ufs_ihash_slock); } makefs/src/sys/ufs/ufs/ufs_inode.c010064400000000000000000000116241026206255500144000ustar00/* $OpenBSD: ufs_inode.c,v 1.29 2005/06/10 17:37:41 pedro Exp $ */ /* $NetBSD: ufs_inode.c,v 1.7 1996/05/11 18:27:52 mycroft Exp $ */ /* * Copyright (c) 1991, 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. * * @(#)ufs_inode.c 8.7 (Berkeley) 7/22/94 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef UFS_DIRHASH #include #include #endif u_long nextgennumber; /* Next generation number to assign. */ /* * Last reference to an inode. If necessary, write or delete it. */ int ufs_inactive(v) void *v; { struct vop_inactive_args /* { struct vnode *a_vp; struct proc *a_p; } */ *ap = v; struct vnode *vp = ap->a_vp; struct inode *ip = VTOI(vp); struct proc *p = ap->a_p; mode_t mode; int error = 0; extern int prtactive; if (prtactive && vp->v_usecount != 0) vprint("ffs_inactive: pushing active", vp); /* * Ignore inodes related to stale file handles. */ if (ip->i_ffs_mode == 0) goto out; if (ip->i_ffs_nlink <= 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { if (getinoquota(ip) == 0) (void)ufs_quota_free_inode(ip, NOCRED); error = UFS_TRUNCATE(ip, (off_t)0, 0, NOCRED); ip->i_ffs_rdev = 0; mode = ip->i_ffs_mode; ip->i_ffs_mode = 0; ip->i_flag |= IN_CHANGE | IN_UPDATE; /* * Setting the mode to zero needs to wait for the inode to be * written just as does a change to the link count. So, rather * than creating a new entry point to do the same thing, we * just use softdep_change_linkcnt(). Also, we can't let * softdep co-opt us to help on its worklist, as we may end up * trying to recycle vnodes and getting to this same point a * couple of times, blowing the kernel stack. However, this * could be optimized by checking if we are coming from * vrele(), vput() or vclean() (by checking for VXLOCK) and * just avoiding the co-opt to happen in the last case. */ if (DOINGSOFTDEP(vp)) softdep_change_linkcnt(ip, 1); UFS_INODE_FREE(ip, ip->i_number, mode); } if (ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) { UFS_UPDATE(ip, 0); } out: VOP_UNLOCK(vp, 0, p); /* * If we are done with the inode, reclaim it * so that it can be reused immediately. */ if (ip->i_ffs_mode == 0) vrecycle(vp, NULL, p); return (error); } /* * Reclaim an inode so that it can be used for other purposes. */ int ufs_reclaim(vp, p) register struct vnode *vp; struct proc *p; { register struct inode *ip; extern int prtactive; if (prtactive && vp->v_usecount != 0) vprint("ufs_reclaim: pushing active", vp); /* * Remove the inode from its hash chain. */ ip = VTOI(vp); ufs_ihashrem(ip); /* * Purge old data structures associated with the inode. */ cache_purge(vp); if (ip->i_devvp) { vrele(ip->i_devvp); } #ifdef UFS_DIRHASH if (ip->i_dirhash != NULL) ufsdirhash_free(ip); #endif ufs_quota_delete(ip); return (0); } makefs/src/sys/ufs/ufs/ufs_lookup.c010064400000000000000000001052461314214547500146220ustar00/* $OpenBSD: ufs_lookup.c,v 1.33 2005/07/20 16:30:35 pedro Exp $ */ /* $NetBSD: ufs_lookup.c,v 1.7 1996/02/09 22:36:06 christos Exp $ */ /* * Copyright (c) 1989, 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. * * @(#)ufs_lookup.c 8.9 (Berkeley) 8/11/94 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef UFS_DIRHASH #include #endif #include #include extern struct nchstats nchstats; #ifdef DIAGNOSTIC int dirchk = 1; #else int dirchk = 0; #endif #define FSFMT(vp) ((vp)->v_mount->mnt_maxsymlinklen <= 0) /* * Convert a component of a pathname into a pointer to a locked inode. * This is a very central and rather complicated routine. * If the filesystem is not maintained in a strict tree hierarchy, * this can result in a deadlock situation (see comments in code below). * * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending * on whether the name is to be looked up, created, renamed, or deleted. * When CREATE, RENAME, or DELETE is specified, information usable in * creating, renaming, or deleting a directory entry may be calculated. * If flag has LOCKPARENT or'ed into it and the target of the pathname * exists, lookup returns both the target and its parent directory locked. * When creating or renaming and LOCKPARENT is specified, the target may * not be ".". When deleting and LOCKPARENT is specified, the target may * be "."., but the caller must check to ensure it does an vrele and vput * instead of two vputs. * * Overall outline of ufs_lookup: * * check accessibility of directory * look for name in cache, if found, then if at end of path * and deleting or creating, drop it, else return name * search for name in directory, to found or notfound * notfound: * if creating, return locked directory, leaving info on available slots * else return error * found: * if at end of path and deleting, return information to allow delete * if at end of path and rewriting (RENAME and LOCKPARENT), lock target * inode and return info to allow rewrite * if not at end, add name to cache; if at end and neither creating * nor deleting, add name to cache */ int ufs_lookup(v) void *v; { struct vop_lookup_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; } */ *ap = v; register struct vnode *vdp; /* vnode for directory being searched */ register struct inode *dp; /* inode for directory being searched */ struct buf *bp; /* a buffer of directory entries */ register struct direct *ep; /* the current directory entry */ int entryoffsetinblock; /* offset of ep in bp's buffer */ enum {NONE, COMPACT, FOUND} slotstatus; doff_t slotoffset; /* offset of area with free space */ int slotsize; /* size of area at slotoffset */ int slotfreespace; /* amount of space free in slot */ int slotneeded; /* size of the entry we're seeking */ int numdirpasses; /* strategy for directory search */ doff_t endsearch; /* offset to end directory search */ doff_t prevoff; /* prev entry dp->i_offset */ struct vnode *pdp; /* saved dp during symlink work */ struct vnode *tdp; /* returned by VFS_VGET */ doff_t enduseful; /* pointer past last used dir slot */ u_long bmask; /* block offset mask */ int lockparent; /* 1 => lockparent flag is set */ int wantparent; /* 1 => wantparent or lockparent flag */ int namlen, error; struct vnode **vpp = ap->a_vpp; struct componentname *cnp = ap->a_cnp; struct ucred *cred = cnp->cn_cred; int flags; int nameiop = cnp->cn_nameiop; struct proc *p = cnp->cn_proc; cnp->cn_flags &= ~PDIRUNLOCK; flags = cnp->cn_flags; bp = NULL; slotoffset = -1; *vpp = NULL; vdp = ap->a_dvp; dp = VTOI(vdp); lockparent = flags & LOCKPARENT; wantparent = flags & (LOCKPARENT|WANTPARENT); /* * Check accessiblity of directory. */ if ((dp->i_ffs_mode & IFMT) != IFDIR) return (ENOTDIR); if ((error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_proc)) != 0) return (error); if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) && (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) return (EROFS); /* * We now have a segment name to search for, and a directory to search. * * Before tediously performing a linear scan of the directory, * check the name cache to see if the directory/name pair * we are looking for is known already. */ if ((error = cache_lookup(vdp, vpp, cnp)) >= 0) return (error); /* * Suppress search for slots unless creating * file and at end of pathname, in which case * we watch for a place to put the new file in * case it doesn't already exist. */ slotstatus = FOUND; slotfreespace = slotsize = slotneeded = 0; if ((nameiop == CREATE || nameiop == RENAME) && (flags & ISLASTCN)) { slotstatus = NONE; slotneeded = (sizeof(struct direct) - MAXNAMLEN + cnp->cn_namelen + 3) &~ 3; } /* * If there is cached information on a previous search of * this directory, pick up where we last left off. * We cache only lookups as these are the most common * and have the greatest payoff. Caching CREATE has little * benefit as it usually must search the entire directory * to determine that the entry does not exist. Caching the * location of the last DELETE or RENAME has not reduced * profiling time and hence has been removed in the interest * of simplicity. */ bmask = VFSTOUFS(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1; #ifdef UFS_DIRHASH /* * Use dirhash for fast operations on large directories. The logic * to determine whether to hash the directory is contained within * ufsdirhash_build(); a zero return means that it decided to hash * this directory and it successfully built up the hash table. */ if (ufsdirhash_build(dp) == 0) { /* Look for a free slot if needed. */ enduseful = dp->i_size; if (slotstatus != FOUND) { slotoffset = ufsdirhash_findfree(dp, slotneeded, &slotsize); if (slotoffset >= 0) { slotstatus = COMPACT; enduseful = ufsdirhash_enduseful(dp); if (enduseful < 0) enduseful = dp->i_size; } } /* Look up the component. */ numdirpasses = 1; entryoffsetinblock = 0; /* silence compiler warning */ switch (ufsdirhash_lookup(dp, cnp->cn_nameptr, cnp->cn_namelen, &dp->i_offset, &bp, nameiop == DELETE ? &prevoff : NULL)) { case 0: ep = (struct direct *)((char *)bp->b_data + (dp->i_offset & bmask)); goto foundentry; case ENOENT: #define roundup2(x, y) (((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */ dp->i_offset = roundup2(dp->i_size, DIRBLKSIZ); goto notfound; default: /* Something failed; just do a linear search. */ break; } } #endif /* UFS_DIRHASH */ if (nameiop != LOOKUP || dp->i_diroff == 0 || dp->i_diroff >= dp->i_ffs_size) { entryoffsetinblock = 0; dp->i_offset = 0; numdirpasses = 1; } else { dp->i_offset = dp->i_diroff; if ((entryoffsetinblock = dp->i_offset & bmask) && (error = UFS_BUFATOFF(dp, (off_t)dp->i_offset, NULL, &bp))) return (error); numdirpasses = 2; nchstats.ncs_2passes++; } prevoff = dp->i_offset; endsearch = roundup(dp->i_ffs_size, DIRBLKSIZ); enduseful = 0; searchloop: while (dp->i_offset < endsearch) { /* * If necessary, get the next directory block. */ if ((dp->i_offset & bmask) == 0) { if (bp != NULL) brelse(bp); error = UFS_BUFATOFF(dp, (off_t)dp->i_offset, NULL, &bp); if (error) return (error); entryoffsetinblock = 0; } /* * If still looking for a slot, and at a DIRBLKSIZE * boundary, have to start looking for free space again. */ if (slotstatus == NONE && (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) { slotoffset = -1; slotfreespace = 0; } /* * Get pointer to next entry. * Full validation checks are slow, so we only check * enough to insure forward progress through the * directory. Complete checks can be run by patching * "dirchk" to be true. */ ep = (struct direct *)((char *)bp->b_data + entryoffsetinblock); if (ep->d_reclen == 0 || (dirchk && ufs_dirbadentry(vdp, ep, entryoffsetinblock))) { int i; ufs_dirbad(dp, dp->i_offset, "mangled entry"); i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)); dp->i_offset += i; entryoffsetinblock += i; continue; } /* * If an appropriate sized slot has not yet been found, * check to see if one is available. Also accumulate space * in the current block so that we can determine if * compaction is viable. */ if (slotstatus != FOUND) { int size = ep->d_reclen; if (ep->d_ino != 0) size -= DIRSIZ(FSFMT(vdp), ep); if (size > 0) { if (size >= slotneeded) { slotstatus = FOUND; slotoffset = dp->i_offset; slotsize = ep->d_reclen; } else if (slotstatus == NONE) { slotfreespace += size; if (slotoffset == -1) slotoffset = dp->i_offset; if (slotfreespace >= slotneeded) { slotstatus = COMPACT; slotsize = dp->i_offset + ep->d_reclen - slotoffset; } } } } /* * Check for a name match. */ if (ep->d_ino) { # if (BYTE_ORDER == LITTLE_ENDIAN) if (vdp->v_mount->mnt_maxsymlinklen > 0) namlen = ep->d_namlen; else namlen = ep->d_type; # else namlen = ep->d_namlen; # endif if (namlen == cnp->cn_namelen && !bcmp(cnp->cn_nameptr, ep->d_name, (unsigned)namlen)) { #ifdef UFS_DIRHASH foundentry: #endif /* * Save directory entry's inode number and * reclen in ndp->ni_ufs area, and release * directory buffer. */ dp->i_ino = ep->d_ino; dp->i_reclen = ep->d_reclen; goto found; } } prevoff = dp->i_offset; dp->i_offset += ep->d_reclen; entryoffsetinblock += ep->d_reclen; if (ep->d_ino) enduseful = dp->i_offset; } #ifdef UFS_DIRHASH notfound: #endif /* * If we started in the middle of the directory and failed * to find our target, we must check the beginning as well. */ if (numdirpasses == 2) { numdirpasses--; dp->i_offset = 0; endsearch = dp->i_diroff; goto searchloop; } if (bp != NULL) brelse(bp); /* * If creating, and at end of pathname and current * directory has not been removed, then can consider * allowing file to be created. */ if ((nameiop == CREATE || nameiop == RENAME) && (flags & ISLASTCN) && dp->i_effnlink != 0) { /* * Access for write is interpreted as allowing * creation of files in the directory. */ error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc); if (error) return (error); /* * Return an indication of where the new directory * entry should be put. If we didn't find a slot, * then set dp->i_count to 0 indicating * that the new slot belongs at the end of the * directory. If we found a slot, then the new entry * can be put in the range from dp->i_offset to * dp->i_offset + dp->i_count. */ if (slotstatus == NONE) { dp->i_offset = roundup(dp->i_ffs_size, DIRBLKSIZ); dp->i_count = 0; enduseful = dp->i_offset; } else if (nameiop == DELETE) { dp->i_offset = slotoffset; if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0) dp->i_count = 0; else dp->i_count = dp->i_offset - prevoff; } else { dp->i_offset = slotoffset; dp->i_count = slotsize; if (enduseful < slotoffset + slotsize) enduseful = slotoffset + slotsize; } dp->i_endoff = roundup(enduseful, DIRBLKSIZ); dp->i_flag |= IN_CHANGE | IN_UPDATE; /* * We return with the directory locked, so that * the parameters we set up above will still be * valid if we actually decide to do a direnter(). * We return ni_vp == NULL to indicate that the entry * does not currently exist; we leave a pointer to * the (locked) directory inode in ndp->ni_dvp. * The pathname buffer is saved so that the name * can be obtained later. * * NB - if the directory is unlocked, then this * information cannot be used. */ cnp->cn_flags |= SAVENAME; if (!lockparent) { VOP_UNLOCK(vdp, 0, p); cnp->cn_flags |= PDIRUNLOCK; } return (EJUSTRETURN); } /* * Insert name into cache (as non-existent) if appropriate. */ if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE) cache_enter(vdp, *vpp, cnp); return (ENOENT); found: if (numdirpasses == 2) nchstats.ncs_pass2++; /* * Check that directory length properly reflects presence * of this entry. */ if (dp->i_offset + DIRSIZ(FSFMT(vdp), ep) > dp->i_ffs_size) { ufs_dirbad(dp, dp->i_offset, "i_ffs_size too small"); dp->i_ffs_size = dp->i_offset + DIRSIZ(FSFMT(vdp), ep); dp->i_flag |= IN_CHANGE | IN_UPDATE; } brelse(bp); /* * Found component in pathname. * If the final component of path name, save information * in the cache as to where the entry was found. */ if ((flags & ISLASTCN) && nameiop == LOOKUP) dp->i_diroff = dp->i_offset &~ (DIRBLKSIZ - 1); /* * If deleting, and at end of pathname, return * parameters which can be used to remove file. * If the wantparent flag isn't set, we return only * the directory (in ndp->ni_dvp), otherwise we go * on and lock the inode, being careful with ".". */ if (nameiop == DELETE && (flags & ISLASTCN)) { /* * Write access to directory required to delete files. */ error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc); if (error) return (error); /* * Return pointer to current entry in dp->i_offset, * and distance past previous entry (if there * is a previous entry in this block) in dp->i_count. * Save directory inode pointer in ndp->ni_dvp for dirremove(). */ if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0) dp->i_count = 0; else dp->i_count = dp->i_offset - prevoff; if (dp->i_number == dp->i_ino) { VREF(vdp); *vpp = vdp; return (0); } error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp); if (error) return (error); /* * If directory is "sticky", then user must own * the directory, or the file in it, else she * may not delete it (unless she's root). This * implements append-only directories. */ if ((dp->i_ffs_mode & ISVTX) && cred->cr_uid != 0 && cred->cr_uid != dp->i_ffs_uid && VTOI(tdp)->i_ffs_uid != cred->cr_uid) { vput(tdp); return (EPERM); } *vpp = tdp; if (!lockparent) { VOP_UNLOCK(vdp, 0, p); cnp->cn_flags |= PDIRUNLOCK; } return (0); } /* * If rewriting (RENAME), return the inode and the * information required to rewrite the present directory * Must get inode of directory entry to verify it's a * regular file, or empty directory. */ if (nameiop == RENAME && wantparent && (flags & ISLASTCN)) { error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc); if (error) return (error); /* * Careful about locking second inode. * This can only occur if the target is ".". */ if (dp->i_number == dp->i_ino) return (EISDIR); error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp); if (error) return (error); *vpp = tdp; cnp->cn_flags |= SAVENAME; if (!lockparent) { VOP_UNLOCK(vdp, 0, p); cnp->cn_flags |= PDIRUNLOCK; } return (0); } /* * Step through the translation in the name. We do not `vput' the * directory because we may need it again if a symbolic link * is relative to the current directory. Instead we save it * unlocked as "pdp". We must get the target inode before unlocking * the directory to insure that the inode will not be removed * before we get it. We prevent deadlock by always fetching * inodes from the root, moving down the directory tree. Thus * when following backward pointers ".." we must unlock the * parent directory before getting the requested directory. * There is a potential race condition here if both the current * and parent directories are removed before the VFS_VGET for the * inode associated with ".." returns. We hope that this occurs * infrequently since we cannot avoid this race condition without * implementing a sophisticated deadlock detection algorithm. * Note also that this simple deadlock detection scheme will not * work if the filesystem has any hard links other than ".." * that point backwards in the directory structure. */ pdp = vdp; if (flags & ISDOTDOT) { VOP_UNLOCK(pdp, 0, p); /* race to get the inode */ cnp->cn_flags |= PDIRUNLOCK; error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp); if (error) { if (vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p) == 0) cnp->cn_flags &= ~PDIRUNLOCK; return (error); } if (lockparent && (flags & ISLASTCN)) { if ((error = vn_lock(pdp, LK_EXCLUSIVE, p))) { vput(tdp); return (error); } cnp->cn_flags &= ~PDIRUNLOCK; } *vpp = tdp; } else if (dp->i_number == dp->i_ino) { VREF(vdp); /* we want ourself, ie "." */ *vpp = vdp; } else { error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp); if (error) return (error); if (!lockparent || !(flags & ISLASTCN)) { VOP_UNLOCK(pdp, 0, p); cnp->cn_flags |= PDIRUNLOCK; } *vpp = tdp; } /* * Insert name into cache if appropriate. */ if (cnp->cn_flags & MAKEENTRY) cache_enter(vdp, *vpp, cnp); return (0); } void ufs_dirbad(ip, offset, how) struct inode *ip; doff_t offset; char *how; { struct mount *mp; mp = ITOV(ip)->v_mount; (void)printf("%s: bad dir ino %d at offset %d: %s\n", mp->mnt_stat.f_mntonname, ip->i_number, offset, how); if ((mp->mnt_stat.f_flags & MNT_RDONLY) == 0) panic("bad dir"); } /* * Do consistency checking on a directory entry: * record length must be multiple of 4 * entry must fit in rest of its DIRBLKSIZ block * record must be large enough to contain entry * name is not longer than MAXNAMLEN * name must be as long as advertised, and null terminated */ int ufs_dirbadentry(dp, ep, entryoffsetinblock) struct vnode *dp; register struct direct *ep; int entryoffsetinblock; { register int i; int namlen; # if (BYTE_ORDER == LITTLE_ENDIAN) if (dp->v_mount->mnt_maxsymlinklen > 0) namlen = ep->d_namlen; else namlen = ep->d_type; # else namlen = ep->d_namlen; # endif if ((ep->d_reclen & 0x3) != 0 || ep->d_reclen > DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)) || ep->d_reclen < DIRSIZ(FSFMT(dp), ep) || namlen > MAXNAMLEN) { /*return (1); */ printf("First bad\n"); goto bad; } if (ep->d_ino == 0) return (0); for (i = 0; i < namlen; i++) if (ep->d_name[i] == '\0') { /*return (1); */ printf("Second bad\n"); goto bad; } if (ep->d_name[i]) goto bad; return (0); bad: return (1); } /* * Construct a new directory entry after a call to namei, using the * parameters that it left in the componentname argument cnp. The * argument ip is the inode to which the new directory entry will refer. */ void ufs_makedirentry(ip, cnp, newdirp) struct inode *ip; struct componentname *cnp; struct direct *newdirp; { #ifdef DIAGNOSTIC if ((cnp->cn_flags & SAVENAME) == 0) panic("ufs_makedirentry: missing name"); #endif newdirp->d_ino = ip->i_number; newdirp->d_namlen = cnp->cn_namelen; bcopy(cnp->cn_nameptr, newdirp->d_name, (unsigned)cnp->cn_namelen + 1); if (ITOV(ip)->v_mount->mnt_maxsymlinklen > 0) newdirp->d_type = IFTODT(ip->i_ffs_mode); else { newdirp->d_type = 0; # if (BYTE_ORDER == LITTLE_ENDIAN) { u_char tmp = newdirp->d_namlen; newdirp->d_namlen = newdirp->d_type; newdirp->d_type = tmp; } # endif } } /* * Write a directory entry after a call to namei, using the parameters * that it left in nameidata. The argument dirp is the new directory * entry contents. Dvp is a pointer to the directory to be written, * which was left locked by namei. Remaining parameters (dp->i_offset, * dp->i_count) indicate how the space for the new entry is to be obtained. * Non-null bp indicates that a directory is being created (for the * soft dependency code). */ int ufs_direnter(dvp, tvp, dirp, cnp, newdirbp) struct vnode *dvp; struct vnode *tvp; struct direct *dirp; struct componentname *cnp; struct buf *newdirbp; { struct ucred *cr; struct proc *p; int newentrysize; struct inode *dp; struct buf *bp; u_int dsize; struct direct *ep, *nep; int error, ret, blkoff, loc, spacefree, flags; char *dirbuf; error = 0; cr = cnp->cn_cred; p = cnp->cn_proc; dp = VTOI(dvp); newentrysize = DIRSIZ(FSFMT(dvp), dirp); if (dp->i_count == 0) { /* * If dp->i_count is 0, then namei could find no * space in the directory. Here, dp->i_offset will * be on a directory block boundary and we will write the * new entry into a fresh block. */ if (dp->i_offset & (DIRBLKSIZ - 1)) panic("ufs_direnter: newblk"); flags = B_CLRBUF; if (!DOINGSOFTDEP(dvp)) flags |= B_SYNC; if ((error = UFS_BUF_ALLOC(dp, (off_t)dp->i_offset, DIRBLKSIZ, cr, flags, &bp)) != 0) { if (DOINGSOFTDEP(dvp) && newdirbp != NULL) bdwrite(newdirbp); return (error); } dp->i_ffs_size = dp->i_offset + DIRBLKSIZ; dp->i_flag |= IN_CHANGE | IN_UPDATE; uvm_vnp_setsize(dvp, dp->i_ffs_size); dirp->d_reclen = DIRBLKSIZ; blkoff = dp->i_offset & (VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_iosize - 1); bcopy((caddr_t)dirp, (caddr_t)bp->b_data + blkoff,newentrysize); #ifdef UFS_DIRHASH if (dp->i_dirhash != NULL) { ufsdirhash_newblk(dp, dp->i_offset); ufsdirhash_add(dp, dirp, dp->i_offset); ufsdirhash_checkblock(dp, (char *)bp->b_data + blkoff, dp->i_offset); } #endif if (DOINGSOFTDEP(dvp)) { /* * Ensure that the entire newly allocated block is a * valid directory so that future growth within the * block does not have to ensure that the block is * written before the inode. */ blkoff += DIRBLKSIZ; while (blkoff < bp->b_bcount) { ((struct direct *) (bp->b_data + blkoff))->d_reclen = DIRBLKSIZ; blkoff += DIRBLKSIZ; } if (softdep_setup_directory_add(bp, dp, dp->i_offset, dirp->d_ino, newdirbp, 1) == 0) { bdwrite(bp); return (UFS_UPDATE(dp, 0)); } /* We have just allocated a directory block in an * indirect block. Rather than tracking when it gets * claimed by the inode, we simply do a VOP_FSYNC * now to ensure that it is there (in case the user * does a future fsync). Note that we have to unlock * the inode for the entry that we just entered, as * the VOP_FSYNC may need to lock other inodes which * can lead to deadlock if we also hold a lock on * the newly entered node. */ if ((error = VOP_BWRITE(bp))) return (error); if (tvp != NULL) VOP_UNLOCK(tvp, 0, p); error = VOP_FSYNC(dvp, p->p_ucred, MNT_WAIT, p); if (tvp != NULL) vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY, p); return (error); } error = VOP_BWRITE(bp); ret = UFS_UPDATE(dp, !DOINGSOFTDEP(dvp)); if (error == 0) return (ret); return (error); } /* * If dp->i_count is non-zero, then namei found space for the new * entry in the range dp->i_offset to dp->i_offset + dp->i_count * in the directory. To use this space, we may have to compact * the entries located there, by copying them together towards the * beginning of the block, leaving the free space in one usable * chunk at the end. */ /* * Increase size of directory if entry eats into new space. * This should never push the size past a new multiple of * DIRBLKSIZE. * * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN. */ if (dp->i_offset + dp->i_count > dp->i_ffs_size) dp->i_ffs_size = dp->i_offset + dp->i_count; /* * Get the block containing the space for the new directory entry. */ if ((error = UFS_BUFATOFF(dp, (off_t)dp->i_offset, &dirbuf, &bp)) != 0) { if (DOINGSOFTDEP(dvp) && newdirbp != NULL) bdwrite(newdirbp); return (error); } /* * Find space for the new entry. In the simple case, the entry at * offset base will have the space. If it does not, then namei * arranged that compacting the region dp->i_offset to * dp->i_offset + dp->i_count would yield the space. */ ep = (struct direct *)dirbuf; dsize = ep->d_ino ? DIRSIZ(FSFMT(dvp), ep) : 0; spacefree = ep->d_reclen - dsize; for (loc = ep->d_reclen; loc < dp->i_count; ) { nep = (struct direct *)(dirbuf + loc); /* Trim the existing slot (NB: dsize may be zero). */ ep->d_reclen = dsize; ep = (struct direct *)((char *)ep + dsize); /* Read nep->d_reclen now as the bcopy() may clobber it. */ loc += nep->d_reclen; if (nep->d_ino == 0) { /* * A mid-block unused entry. Such entries are * never created by the kernel, but fsck_ffs * can create them (and it doesn't fix them). * * Add up the free space, and initialise the * relocated entry since we don't bcopy it. */ spacefree += nep->d_reclen; ep->d_ino = 0; dsize = 0; continue; } dsize = DIRSIZ(FSFMT(dvp), nep); spacefree += nep->d_reclen - dsize; #ifdef UFS_DIRHASH if (dp->i_dirhash != NULL) ufsdirhash_move(dp, nep, dp->i_offset + ((char *)nep - dirbuf), dp->i_offset + ((char *)ep - dirbuf)); #endif if (DOINGSOFTDEP(dvp)) softdep_change_directoryentry_offset(dp, dirbuf, (caddr_t)nep, (caddr_t)ep, dsize); else bcopy((caddr_t)nep, (caddr_t)ep, dsize); } /* * Here, `ep' points to a directory entry containing `dsize' in-use * bytes followed by `spacefree' unused bytes. If ep->d_ino == 0, * then the entry is completely unused (dsize == 0). The value * of ep->d_reclen is always indeterminate. * * Update the pointer fields in the previous entry (if any), * copy in the new entry, and write out the block. */ if (ep->d_ino == 0) { if (spacefree + dsize < newentrysize) panic("ufs_direnter: compact1"); dirp->d_reclen = spacefree + dsize; } else { if (spacefree < newentrysize) panic("ufs_direnter: compact2"); dirp->d_reclen = spacefree; ep->d_reclen = dsize; ep = (struct direct *)((char *)ep + dsize); } #ifdef UFS_DIRHASH if (dp->i_dirhash != NULL && (ep->d_ino == 0 || dirp->d_reclen == spacefree)) ufsdirhash_add(dp, dirp, dp->i_offset + ((char *)ep - dirbuf)); #endif bcopy((caddr_t)dirp, (caddr_t)ep, (u_int)newentrysize); #ifdef UFS_DIRHASH if (dp->i_dirhash != NULL) ufsdirhash_checkblock(dp, dirbuf - (dp->i_offset & (DIRBLKSIZ - 1)), dp->i_offset & ~(DIRBLKSIZ - 1)); #endif if (DOINGSOFTDEP(dvp)) { (void)softdep_setup_directory_add(bp, dp, dp->i_offset + (caddr_t)ep - dirbuf, dirp->d_ino, newdirbp, 0); bdwrite(bp); } else { error = VOP_BWRITE(bp); } dp->i_flag |= IN_CHANGE | IN_UPDATE; /* * If all went well, and the directory can be shortened, proceed * with the truncation. Note that we have to unlock the inode for * the entry that we just entered, as the truncation may need to * lock other inodes which can lead to deadlock if we also hold a * lock on the newly entered node. */ if (error == 0 && dp->i_endoff && dp->i_endoff < dp->i_ffs_size) { if (tvp != NULL) VOP_UNLOCK(tvp, 0, p); #ifdef UFS_DIRHASH if (dp->i_dirhash != NULL) ufsdirhash_dirtrunc(dp, dp->i_endoff); #endif error = UFS_TRUNCATE(dp, (off_t)dp->i_endoff, IO_SYNC, cr); if (tvp != NULL) vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY, p); } return (error); } /* * Remove a directory entry after a call to namei, using * the parameters which it left in nameidata. The entry * dp->i_offset contains the offset into the directory of the * entry to be eliminated. The dp->i_count field contains the * size of the previous record in the directory. If this * is 0, the first entry is being deleted, so we need only * zero the inode number to mark the entry as free. If the * entry is not the first in the directory, we must reclaim * the space of the now empty record by adding the record size * to the size of the previous entry. */ int ufs_dirremove(dvp, ip, flags, isrmdir) struct vnode *dvp; struct inode *ip; int flags; int isrmdir; { struct inode *dp; struct direct *ep; struct buf *bp; int error; dp = VTOI(dvp); if ((error = UFS_BUFATOFF(dp, (off_t)(dp->i_offset - dp->i_count), (char **)&ep, &bp)) != 0) return (error); #ifdef UFS_DIRHASH /* * Remove the dirhash entry. This is complicated by the fact * that `ep' is the previous entry when dp->i_count != 0. */ if (dp->i_dirhash != NULL) ufsdirhash_remove(dp, (dp->i_count == 0) ? ep : (struct direct *)((char *)ep + ep->d_reclen), dp->i_offset); #endif if (dp->i_count == 0) { /* * First entry in block: set d_ino to zero. */ ep->d_ino = 0; } else { /* * Collapse new free space into previous entry. */ ep->d_reclen += dp->i_reclen; } #ifdef UFS_DIRHASH if (dp->i_dirhash != NULL) ufsdirhash_checkblock(dp, (char *)ep - ((dp->i_offset - dp->i_count) & (DIRBLKSIZ - 1)), dp->i_offset & ~(DIRBLKSIZ - 1)); #endif if (DOINGSOFTDEP(dvp)) { if (ip) { ip->i_effnlink--; softdep_change_linkcnt(ip, 0); softdep_setup_remove(bp, dp, ip, isrmdir); } if (softdep_slowdown(dvp)) { error = bwrite(bp); } else { bdwrite(bp); error = 0; } } else { if (ip) { ip->i_effnlink--; ip->i_ffs_nlink--; ip->i_flag |= IN_CHANGE; } if (DOINGASYNC(dvp) && dp->i_count != 0) { bdwrite(bp); error = 0; } else error = bwrite(bp); } dp->i_flag |= IN_CHANGE | IN_UPDATE; return (error); } /* * Rewrite an existing directory entry to point at the inode * supplied. The parameters describing the directory entry are * set up by a call to namei. */ int ufs_dirrewrite(dp, oip, newinum, newtype, isrmdir) struct inode *dp, *oip; ino_t newinum; int newtype; int isrmdir; { struct buf *bp; struct direct *ep; struct vnode *vdp = ITOV(dp); int error; error = UFS_BUFATOFF(dp, (off_t)dp->i_offset, (char **)&ep, &bp); if (error) return (error); ep->d_ino = newinum; if (vdp->v_mount->mnt_maxsymlinklen > 0) ep->d_type = newtype; oip->i_effnlink--; if (DOINGSOFTDEP(vdp)) { softdep_change_linkcnt(oip, 0); softdep_setup_directory_change(bp, dp, oip, newinum, isrmdir); bdwrite(bp); } else { oip->i_ffs_nlink--; oip->i_flag |= IN_CHANGE; if (DOINGASYNC(vdp)) { bdwrite(bp); error = 0; } else { error = VOP_BWRITE(bp); } } dp->i_flag |= IN_CHANGE | IN_UPDATE; return (error); } /* * Check if a directory is empty or not. * Inode supplied must be locked. * * Using a struct dirtemplate here is not precisely * what we want, but better than using a struct direct. * * NB: does not handle corrupted directories. */ int ufs_dirempty(ip, parentino, cred) struct inode *ip; ino_t parentino; struct ucred *cred; { off_t off, m; struct dirtemplate dbuf; struct direct *dp = (struct direct *)&dbuf; int error, namlen; size_t count; #define MINDIRSIZ (sizeof (struct dirtemplate) / 2) m = ip->i_ffs_size; for (off = 0; off < m; off += dp->d_reclen) { error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, off, UIO_SYSSPACE, IO_NODELOCKED, cred, &count, (struct proc *)0); /* * Since we read MINDIRSIZ, residual must * be 0 unless we're at end of file. */ if (error || count != 0) return (0); /* avoid infinite loops */ if (dp->d_reclen == 0) return (0); /* skip empty entries */ if (dp->d_ino == 0) continue; /* accept only "." and ".." */ # if (BYTE_ORDER == LITTLE_ENDIAN) if (ITOV(ip)->v_mount->mnt_maxsymlinklen > 0) namlen = dp->d_namlen; else namlen = dp->d_type; # else namlen = dp->d_namlen; # endif if (namlen > 2) return (0); if (dp->d_name[0] != '.') return (0); /* * At this point namlen must be 1 or 2. * 1 implies ".", 2 implies ".." if second * char is also "." */ if (namlen == 1 && dp->d_ino == ip->i_number) continue; if (dp->d_name[1] == '.' && dp->d_ino == parentino) continue; return (0); } return (1); } /* * Check if source directory is in the path of the target directory. * Target is supplied locked, source is unlocked. * The target is always vput before returning. */ int ufs_checkpath(source, target, cred) struct inode *source, *target; struct ucred *cred; { struct vnode *vp; int error, rootino, namlen; struct dirtemplate dirbuf; vp = ITOV(target); if (target->i_number == source->i_number) { error = EEXIST; goto out; } rootino = ROOTINO; error = 0; if (target->i_number == rootino) goto out; for (;;) { if (vp->v_type != VDIR) { error = ENOTDIR; break; } error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf, sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE, IO_NODELOCKED, cred, NULL, (struct proc *)0); if (error != 0) break; # if (BYTE_ORDER == LITTLE_ENDIAN) if (vp->v_mount->mnt_maxsymlinklen > 0) namlen = dirbuf.dotdot_namlen; else namlen = dirbuf.dotdot_type; # else namlen = dirbuf.dotdot_namlen; # endif if (namlen != 2 || dirbuf.dotdot_name[0] != '.' || dirbuf.dotdot_name[1] != '.') { error = ENOTDIR; break; } if (dirbuf.dotdot_ino == source->i_number) { error = EINVAL; break; } if (dirbuf.dotdot_ino == rootino) break; vput(vp); error = VFS_VGET(vp->v_mount, dirbuf.dotdot_ino, &vp); if (error) { vp = NULL; break; } } out: if (error == ENOTDIR) printf("checkpath: .. not a directory\n"); if (vp != NULL) vput(vp); return (error); } makefs/src/sys/ufs/ufs/ufs_quota.c010064400000000000000000000640701314214547600144420ustar00/* $OpenBSD: ufs_quota.c,v 1.18 2004/12/26 21:22:14 miod Exp $ */ /* $NetBSD: ufs_quota.c,v 1.8 1996/02/09 22:36:09 christos Exp $ */ /* * Copyright (c) 1982, 1986, 1990, 1993, 1995 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Robert Elz at The University of Melbourne. * * 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. * * @(#)ufs_quota.c 8.5 (Berkeley) 8/19/94 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * The following structure records disk usage for a user or group on a * filesystem. There is one allocated for each quota that exists on any * filesystem for the current user or group. A cache is kept of recently * used entries. */ struct dquot { LIST_ENTRY(dquot) dq_hash; /* hash list */ TAILQ_ENTRY(dquot) dq_freelist; /* free list */ u_int16_t dq_flags; /* flags, see below */ u_int16_t dq_cnt; /* count of active references */ u_int16_t dq_spare; /* unused spare padding */ u_int16_t dq_type; /* quota type of this dquot */ u_int32_t dq_id; /* identifier this applies to */ struct vnode *dq_vp; /* file backing this quota */ struct ucred *dq_cred; /* credentials for writing file */ struct dqblk dq_dqb; /* actual usage & quotas */ }; /* * Flag values. */ #define DQ_LOCK 0x01 /* this quota locked (no MODS) */ #define DQ_WANT 0x02 /* wakeup on unlock */ #define DQ_MOD 0x04 /* this quota modified since read */ #define DQ_FAKE 0x08 /* no limits here, just usage */ #define DQ_BLKS 0x10 /* has been warned about blk limit */ #define DQ_INODS 0x20 /* has been warned about inode limit */ /* * Shorthand notation. */ #define dq_bhardlimit dq_dqb.dqb_bhardlimit #define dq_bsoftlimit dq_dqb.dqb_bsoftlimit #define dq_curblocks dq_dqb.dqb_curblocks #define dq_ihardlimit dq_dqb.dqb_ihardlimit #define dq_isoftlimit dq_dqb.dqb_isoftlimit #define dq_curinodes dq_dqb.dqb_curinodes #define dq_btime dq_dqb.dqb_btime #define dq_itime dq_dqb.dqb_itime /* * If the system has never checked for a quota for this file, then it is * set to NODQUOT. Once a write attempt is made the inode pointer is set * to reference a dquot structure. */ #define NODQUOT NULL void dqref(struct dquot *); void dqrele(struct vnode *, struct dquot *); int dqsync(struct vnode *, struct dquot *); #ifdef DIAGNOSTIC void chkdquot(struct inode *); #endif int getquota(struct mount *, u_long, int, caddr_t); int quotaon(struct proc *, struct mount *, int, caddr_t); int setquota(struct mount *, u_long, int, caddr_t); int setuse(struct mount *, u_long, int, caddr_t); int chkdqchg(struct inode *, long, struct ucred *, int); int chkiqchg(struct inode *, long, struct ucred *, int); int dqget(struct vnode *, u_long, struct ufsmount *, int, struct dquot **); int quotaon_vnode(struct vnode *, void *); int quotaoff_vnode(struct vnode *, void *); int qsync_vnode(struct vnode *, void *); /* * Quota name to error message mapping. */ static char *quotatypes[] = INITQFNAMES; /* * Obtain a reference to a dquot. */ void dqref(dq) struct dquot *dq; { dq->dq_cnt++; } /* * Set up the quotas for an inode. * * This routine completely defines the semantics of quotas. * If other criterion want to be used to establish quotas, the * MAXQUOTAS value in quotas.h should be increased, and the * additional dquots set up here. */ int getinoquota(ip) struct inode *ip; { struct ufsmount *ump; struct vnode *vp = ITOV(ip); int error; ump = ip->i_ump; /* * Set up the user quota based on file uid. * EINVAL means that quotas are not enabled. */ if (ip->i_dquot[USRQUOTA] == NODQUOT && (error = dqget(vp, ip->i_ffs_uid, ump, USRQUOTA, &ip->i_dquot[USRQUOTA])) && error != EINVAL) return (error); /* * Set up the group quota based on file gid. * EINVAL means that quotas are not enabled. */ if (ip->i_dquot[GRPQUOTA] == NODQUOT && (error = dqget(vp, ip->i_ffs_gid, ump, GRPQUOTA, &ip->i_dquot[GRPQUOTA])) && error != EINVAL) return (error); return (0); } /* * Update disk usage, and take corrective action. */ int ufs_quota_alloc_blocks2(struct inode *ip, int32_t change, struct ucred *cred, enum ufs_quota_flags flags) { struct dquot *dq; int i; int error; #ifdef DIAGNOSTIC chkdquot(ip); #endif if (change == 0) return (0); if ((flags & UFS_QUOTA_FORCE) == 0 && (cred != NOCRED && cred->cr_uid != 0)) { for (i = 0; i < MAXQUOTAS; i++) { if (flags & (1 << i)) continue; if ((dq = ip->i_dquot[i]) == NODQUOT) continue; if ((error = chkdqchg(ip, change, cred, i)) != 0) return (error); } } for (i = 0; i < MAXQUOTAS; i++) { if (flags & (1 << i)) continue; if ((dq = ip->i_dquot[i]) == NODQUOT) continue; while (dq->dq_flags & DQ_LOCK) { dq->dq_flags |= DQ_WANT; (void) tsleep(dq, PINOD+1, "chkdq", 0); } dq->dq_curblocks += change; dq->dq_flags |= DQ_MOD; } return (0); } int ufs_quota_free_blocks2(struct inode *ip, int32_t change, struct ucred *cred, enum ufs_quota_flags flags) { struct dquot *dq; int i; #ifdef DIAGNOSTIC if (!VOP_ISLOCKED(ITOV(ip))) panic ("ufs_quota_free_blocks2: vnode is not locked"); #endif if (change == 0) return (0); for (i = 0; i < MAXQUOTAS; i++) { if (flags & (1 << i)) continue; if ((dq = ip->i_dquot[i]) == NODQUOT) continue; while (dq->dq_flags & DQ_LOCK) { dq->dq_flags |= DQ_WANT; (void) tsleep(dq, PINOD+1, "chkdq", 0); } if (dq->dq_curblocks >= change) dq->dq_curblocks -= change; else dq->dq_curblocks = 0; dq->dq_flags &= ~DQ_BLKS; dq->dq_flags |= DQ_MOD; } return (0); } /* * Check for a valid change to a users allocation. * Issue an error message if appropriate. */ int chkdqchg(ip, change, cred, type) struct inode *ip; long change; struct ucred *cred; int type; { struct dquot *dq = ip->i_dquot[type]; long ncurblocks = dq->dq_curblocks + change; /* * If user would exceed their hard limit, disallow space allocation. */ if (ncurblocks >= dq->dq_bhardlimit && dq->dq_bhardlimit) { if ((dq->dq_flags & DQ_BLKS) == 0 && ip->i_ffs_uid == cred->cr_uid) { uprintf("\n%s: write failed, %s disk limit reached\n", ITOV(ip)->v_mount->mnt_stat.f_mntonname, quotatypes[type]); dq->dq_flags |= DQ_BLKS; } return (EDQUOT); } /* * If user is over their soft limit for too long, disallow space * allocation. Reset time limit as they cross their soft limit. */ if (ncurblocks >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) { if (dq->dq_curblocks < dq->dq_bsoftlimit) { dq->dq_btime = time.tv_sec + ip->i_ump->um_btime[type]; if (ip->i_ffs_uid == cred->cr_uid) uprintf("\n%s: warning, %s %s\n", ITOV(ip)->v_mount->mnt_stat.f_mntonname, quotatypes[type], "disk quota exceeded"); return (0); } if (time.tv_sec > dq->dq_btime) { if ((dq->dq_flags & DQ_BLKS) == 0 && ip->i_ffs_uid == cred->cr_uid) { uprintf("\n%s: write failed, %s %s\n", ITOV(ip)->v_mount->mnt_stat.f_mntonname, quotatypes[type], "disk quota exceeded for too long"); dq->dq_flags |= DQ_BLKS; } return (EDQUOT); } } return (0); } /* * Check the inode limit, applying corrective action. */ int ufs_quota_alloc_inode2(struct inode *ip, struct ucred *cred, enum ufs_quota_flags flags) { struct dquot *dq; int i; int error; #ifdef DIAGNOSTIC chkdquot(ip); #endif if ((flags & UFS_QUOTA_FORCE) == 0 && cred->cr_uid != 0) { for (i = 0; i < MAXQUOTAS; i++) { if (flags & (1 << i)) continue; if ((dq = ip->i_dquot[i]) == NODQUOT) continue; if ((error = chkiqchg(ip, 1, cred, i)) != 0) return (error); } } for (i = 0; i < MAXQUOTAS; i++) { if (flags & (1 << i)) continue; if ((dq = ip->i_dquot[i]) == NODQUOT) continue; while (dq->dq_flags & DQ_LOCK) { dq->dq_flags |= DQ_WANT; (void) tsleep(dq, PINOD+1, "chkiq", 0); } dq->dq_curinodes++; dq->dq_flags |= DQ_MOD; } return (0); } int ufs_quota_free_inode2(struct inode *ip, struct ucred *cred, enum ufs_quota_flags flags) { struct dquot *dq; int i; #ifdef DIAGNOSTIC if (!VOP_ISLOCKED(ITOV(ip))) panic ("ufs_quota_free_blocks2: vnode is not locked"); #endif for (i = 0; i < MAXQUOTAS; i++) { if (flags & (1 << i)) continue; if ((dq = ip->i_dquot[i]) == NODQUOT) continue; while (dq->dq_flags & DQ_LOCK) { dq->dq_flags |= DQ_WANT; (void) tsleep(dq, PINOD+1, "chkiq", 0); } if (dq->dq_curinodes > 0) dq->dq_curinodes--; dq->dq_flags &= ~DQ_INODS; dq->dq_flags |= DQ_MOD; } return (0); } /* * Check for a valid change to a users allocation. * Issue an error message if appropriate. */ int chkiqchg(ip, change, cred, type) struct inode *ip; long change; struct ucred *cred; int type; { struct dquot *dq = ip->i_dquot[type]; long ncurinodes = dq->dq_curinodes + change; /* * If user would exceed their hard limit, disallow inode allocation. */ if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) { if ((dq->dq_flags & DQ_INODS) == 0 && ip->i_ffs_uid == cred->cr_uid) { uprintf("\n%s: write failed, %s inode limit reached\n", ITOV(ip)->v_mount->mnt_stat.f_mntonname, quotatypes[type]); dq->dq_flags |= DQ_INODS; } return (EDQUOT); } /* * If user is over their soft limit for too long, disallow inode * allocation. Reset time limit as they cross their soft limit. */ if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) { if (dq->dq_curinodes < dq->dq_isoftlimit) { dq->dq_itime = time.tv_sec + ip->i_ump->um_itime[type]; if (ip->i_ffs_uid == cred->cr_uid) uprintf("\n%s: warning, %s %s\n", ITOV(ip)->v_mount->mnt_stat.f_mntonname, quotatypes[type], "inode quota exceeded"); return (0); } if (time.tv_sec > dq->dq_itime) { if ((dq->dq_flags & DQ_INODS) == 0 && ip->i_ffs_uid == cred->cr_uid) { uprintf("\n%s: write failed, %s %s\n", ITOV(ip)->v_mount->mnt_stat.f_mntonname, quotatypes[type], "inode quota exceeded for too long"); dq->dq_flags |= DQ_INODS; } return (EDQUOT); } } return (0); } #ifdef DIAGNOSTIC /* * On filesystems with quotas enabled, it is an error for a file to change * size and not to have a dquot structure associated with it. */ void chkdquot(ip) struct inode *ip; { struct ufsmount *ump = ip->i_ump; int i; struct vnode *vp = ITOV(ip); if (!VOP_ISLOCKED(vp)) panic ("chkdquot: vnode is not locked"); for (i = 0; i < MAXQUOTAS; i++) { if (ump->um_quotas[i] == NULLVP || (ump->um_qflags[i] & (QTF_OPENING|QTF_CLOSING))) continue; if (ip->i_dquot[i] == NODQUOT) { vprint("chkdquot: missing dquot", ITOV(ip)); panic("missing dquot"); } } } #endif /* * Code to process quotactl commands. */ int quotaon_vnode(struct vnode *vp, void *arg) { int error; struct proc *p = (struct proc *)arg; if (vp->v_type == VNON || vp->v_writecount == 0) { simple_unlock(&vp->v_interlock); return (0); } if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, p)) { return (0); } error = getinoquota(VTOI(vp)); vput(vp); return (error); } /* * Q_QUOTAON - set up a quota file for a particular filesystem. */ int quotaon(p, mp, type, fname) struct proc *p; struct mount *mp; int type; caddr_t fname; { struct ufsmount *ump = VFSTOUFS(mp); struct vnode *vp, **vpp; struct dquot *dq; int error; struct nameidata nd; #ifdef DIAGNOSTIC if (!vfs_isbusy(mp)) panic ("quotaon: mount point not busy"); #endif vpp = &ump->um_quotas[type]; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, fname, p); if ((error = vn_open(&nd, FREAD|FWRITE, 0)) != 0) return (error); vp = nd.ni_vp; VOP_UNLOCK(vp, 0, p); if (vp->v_type != VREG) { (void) vn_close(vp, FREAD|FWRITE, p->p_ucred, p); return (EACCES); } if (*vpp != vp) quotaoff(p, mp, type); ump->um_qflags[type] |= QTF_OPENING; mp->mnt_flag |= MNT_QUOTA; vp->v_flag |= VSYSTEM; *vpp = vp; /* * Save the credential of the process that turned on quotas. * Set up the time limits for this quota. */ crhold(p->p_ucred); ump->um_cred[type] = p->p_ucred; ump->um_btime[type] = MAX_DQ_TIME; ump->um_itime[type] = MAX_IQ_TIME; if (dqget(NULLVP, 0, ump, type, &dq) == 0) { if (dq->dq_btime > 0) ump->um_btime[type] = dq->dq_btime; if (dq->dq_itime > 0) ump->um_itime[type] = dq->dq_itime; dqrele(NULLVP, dq); } /* * Search vnodes associated with this mount point, * adding references to quota file being opened. * NB: only need to add dquot's for inodes being modified. */ error = vfs_mount_foreach_vnode(mp, quotaon_vnode, p); ump->um_qflags[type] &= ~QTF_OPENING; if (error) quotaoff(p, mp, type); return (error); } struct quotaoff_arg { struct proc *p; int type; }; int quotaoff_vnode(struct vnode *vp, void *arg) { struct quotaoff_arg *qa = (struct quotaoff_arg *)arg; struct inode *ip; struct dquot *dq; if (vp->v_type == VNON) { simple_unlock(&vp->v_interlock); return (0); } if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, qa->p)) return (0); ip = VTOI(vp); dq = ip->i_dquot[qa->type]; ip->i_dquot[qa->type] = NODQUOT; dqrele(vp, dq); vput(vp); return (0); } /* * Q_QUOTAOFF - turn off disk quotas for a filesystem. */ int quotaoff(p, mp, type) struct proc *p; struct mount *mp; int type; { struct vnode *qvp; struct ufsmount *ump = VFSTOUFS(mp); struct quotaoff_arg qa; int error; #ifdef DIAGNOSTIC if (!vfs_isbusy(mp)) panic ("quotaoff: mount point not busy"); #endif if ((qvp = ump->um_quotas[type]) == NULLVP) return (0); ump->um_qflags[type] |= QTF_CLOSING; /* * Search vnodes associated with this mount point, * deleting any references to quota file being closed. */ qa.p = p; qa.type = type; vfs_mount_foreach_vnode(mp, quotaoff_vnode, &qa); error = vn_close(qvp, FREAD|FWRITE, p->p_ucred, p); ump->um_quotas[type] = NULLVP; crfree(ump->um_cred[type]); ump->um_cred[type] = NOCRED; ump->um_qflags[type] &= ~QTF_CLOSING; for (type = 0; type < MAXQUOTAS; type++) if (ump->um_quotas[type] != NULLVP) break; if (type == MAXQUOTAS) mp->mnt_flag &= ~MNT_QUOTA; return (error); } /* * Q_GETQUOTA - return current values in a dqblk structure. */ int getquota(mp, id, type, addr) struct mount *mp; u_long id; int type; caddr_t addr; { struct dquot *dq; int error; if ((error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq)) != 0) return (error); error = copyout((caddr_t)&dq->dq_dqb, addr, sizeof (struct dqblk)); dqrele(NULLVP, dq); return (error); } /* * Q_SETQUOTA - assign an entire dqblk structure. */ int setquota(mp, id, type, addr) struct mount *mp; u_long id; int type; caddr_t addr; { struct dquot *dq; struct dquot *ndq; struct ufsmount *ump = VFSTOUFS(mp); struct dqblk newlim; int error; error = copyin(addr, (caddr_t)&newlim, sizeof (struct dqblk)); if (error) return (error); if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0) return (error); dq = ndq; while (dq->dq_flags & DQ_LOCK) { dq->dq_flags |= DQ_WANT; (void) tsleep(dq, PINOD+1, "setquota", 0); } /* * Copy all but the current values. * Reset time limit if previously had no soft limit or were * under it, but now have a soft limit and are over it. */ newlim.dqb_curblocks = dq->dq_curblocks; newlim.dqb_curinodes = dq->dq_curinodes; if (dq->dq_id != 0) { newlim.dqb_btime = dq->dq_btime; newlim.dqb_itime = dq->dq_itime; } if (newlim.dqb_bsoftlimit && dq->dq_curblocks >= newlim.dqb_bsoftlimit && (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit)) newlim.dqb_btime = time.tv_sec + ump->um_btime[type]; if (newlim.dqb_isoftlimit && dq->dq_curinodes >= newlim.dqb_isoftlimit && (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit)) newlim.dqb_itime = time.tv_sec + ump->um_itime[type]; dq->dq_dqb = newlim; if (dq->dq_curblocks < dq->dq_bsoftlimit) dq->dq_flags &= ~DQ_BLKS; if (dq->dq_curinodes < dq->dq_isoftlimit) dq->dq_flags &= ~DQ_INODS; if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 && dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0) dq->dq_flags |= DQ_FAKE; else dq->dq_flags &= ~DQ_FAKE; dq->dq_flags |= DQ_MOD; dqrele(NULLVP, dq); return (0); } /* * Q_SETUSE - set current inode and block usage. */ int setuse(mp, id, type, addr) struct mount *mp; u_long id; int type; caddr_t addr; { struct dquot *dq; struct ufsmount *ump = VFSTOUFS(mp); struct dquot *ndq; struct dqblk usage; int error; error = copyin(addr, (caddr_t)&usage, sizeof (struct dqblk)); if (error) return (error); if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0) return (error); dq = ndq; while (dq->dq_flags & DQ_LOCK) { dq->dq_flags |= DQ_WANT; (void) tsleep(dq, PINOD+1, "setuse", 0); } /* * Reset time limit if have a soft limit and were * previously under it, but are now over it. */ if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit && usage.dqb_curblocks >= dq->dq_bsoftlimit) dq->dq_btime = time.tv_sec + ump->um_btime[type]; if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit && usage.dqb_curinodes >= dq->dq_isoftlimit) dq->dq_itime = time.tv_sec + ump->um_itime[type]; dq->dq_curblocks = usage.dqb_curblocks; dq->dq_curinodes = usage.dqb_curinodes; if (dq->dq_curblocks < dq->dq_bsoftlimit) dq->dq_flags &= ~DQ_BLKS; if (dq->dq_curinodes < dq->dq_isoftlimit) dq->dq_flags &= ~DQ_INODS; dq->dq_flags |= DQ_MOD; dqrele(NULLVP, dq); return (0); } int qsync_vnode(struct vnode *vp, void *arg) { int i; struct proc *p = curproc; struct dquot *dq; if (vp->v_type == VNON) { simple_unlock(&vp->v_interlock); return (0); } if (vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, p)) return (0); for (i = 0; i < MAXQUOTAS; i++) { dq = VTOI(vp)->i_dquot[i]; if (dq != NODQUOT && (dq->dq_flags & DQ_MOD)) dqsync(vp, dq); } vput(vp); return (0); } /* * Q_SYNC - sync quota files to disk. */ int qsync(mp) struct mount *mp; { struct ufsmount *ump = VFSTOUFS(mp); int i; /* * Check if the mount point has any quotas. * If not, simply return. */ for (i = 0; i < MAXQUOTAS; i++) if (ump->um_quotas[i] != NULLVP) break; if (i == MAXQUOTAS) return (0); /* * Search vnodes associated with this mount point, * synchronizing any modified dquot structures. */ vfs_mount_foreach_vnode(mp, qsync_vnode, NULL); return (0); } /* * Code pertaining to management of the in-core dquot data structures. */ #define DQHASH(dqvp, id) \ (&dqhashtbl[((((long)(dqvp)) >> 8) + id) & dqhash]) LIST_HEAD(dqhash, dquot) *dqhashtbl; u_long dqhash; /* * Dquot free list. */ #define DQUOTINC 5 /* minimum free dquots desired */ TAILQ_HEAD(dqfreelist, dquot) dqfreelist; long numdquot, desireddquot = DQUOTINC; /* * Initialize the quota system. */ void ufs_quota_init() { dqhashtbl = hashinit(desiredvnodes, M_DQUOT, M_WAITOK, &dqhash); TAILQ_INIT(&dqfreelist); } /* * Obtain a dquot structure for the specified identifier and quota file * reading the information from the file if necessary. */ int dqget(vp, id, ump, type, dqp) struct vnode *vp; u_long id; struct ufsmount *ump; int type; struct dquot **dqp; { struct proc *p = curproc; struct dquot *dq; struct dqhash *dqh; struct vnode *dqvp; struct iovec aiov; struct uio auio; int error; dqvp = ump->um_quotas[type]; if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) { *dqp = NODQUOT; return (EINVAL); } /* * Check the cache first. */ dqh = DQHASH(dqvp, id); LIST_FOREACH(dq, dqh, dq_hash) { if (dq->dq_id != id || dq->dq_vp != dqvp) continue; /* * Cache hit with no references. Take * the structure off the free list. */ if (dq->dq_cnt == 0) TAILQ_REMOVE(&dqfreelist, dq, dq_freelist); dqref(dq); *dqp = dq; return (0); } /* * Not in cache, allocate a new one. */ if (TAILQ_FIRST(&dqfreelist) == NODQUOT && numdquot < MAXQUOTAS * desiredvnodes) desireddquot += DQUOTINC; if (numdquot < desireddquot) { dq = (struct dquot *)malloc(sizeof *dq, M_DQUOT, M_WAITOK); bzero((char *)dq, sizeof *dq); numdquot++; } else { if ((dq = TAILQ_FIRST(&dqfreelist)) == NULL) { tablefull("dquot"); *dqp = NODQUOT; return (EUSERS); } if (dq->dq_cnt || (dq->dq_flags & DQ_MOD)) panic("free dquot isn't"); TAILQ_REMOVE(&dqfreelist, dq, dq_freelist); LIST_REMOVE(dq, dq_hash); crfree(dq->dq_cred); dq->dq_cred = NOCRED; } /* * Initialize the contents of the dquot structure. */ if (vp != dqvp) vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, p); LIST_INSERT_HEAD(dqh, dq, dq_hash); dqref(dq); dq->dq_flags = DQ_LOCK; dq->dq_id = id; dq->dq_vp = dqvp; dq->dq_type = type; crhold(ump->um_cred[type]); dq->dq_cred = ump->um_cred[type]; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; aiov.iov_base = (caddr_t)&dq->dq_dqb; aiov.iov_len = sizeof (struct dqblk); auio.uio_resid = sizeof (struct dqblk); auio.uio_offset = (off_t)(id * sizeof (struct dqblk)); auio.uio_segflg = UIO_SYSSPACE; auio.uio_rw = UIO_READ; auio.uio_procp = (struct proc *)0; error = VOP_READ(dqvp, &auio, 0, dq->dq_cred); if (auio.uio_resid == sizeof(struct dqblk) && error == 0) bzero((caddr_t)&dq->dq_dqb, sizeof(struct dqblk)); if (vp != dqvp) VOP_UNLOCK(dqvp, 0, p); if (dq->dq_flags & DQ_WANT) wakeup(dq); dq->dq_flags = 0; /* * I/O error in reading quota file, release * quota structure and reflect problem to caller. */ if (error) { LIST_REMOVE(dq, dq_hash); dqrele(vp, dq); *dqp = NODQUOT; return (error); } /* * Check for no limit to enforce. * Initialize time values if necessary. */ if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 && dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0) dq->dq_flags |= DQ_FAKE; if (dq->dq_id != 0) { if (dq->dq_btime == 0) dq->dq_btime = time.tv_sec + ump->um_btime[type]; if (dq->dq_itime == 0) dq->dq_itime = time.tv_sec + ump->um_itime[type]; } *dqp = dq; return (0); } /* * Release a reference to a dquot. */ void dqrele(vp, dq) struct vnode *vp; struct dquot *dq; { if (dq == NODQUOT) return; if (dq->dq_cnt > 1) { dq->dq_cnt--; return; } if (dq->dq_flags & DQ_MOD) (void) dqsync(vp, dq); if (--dq->dq_cnt > 0) return; TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist); } /* * Update the disk quota in the quota file. */ int dqsync(vp, dq) struct vnode *vp; struct dquot *dq; { struct proc *p = curproc; struct vnode *dqvp; struct iovec aiov; struct uio auio; int error; if (dq == NODQUOT) panic("dqsync: dquot"); if ((dq->dq_flags & DQ_MOD) == 0) return (0); if ((dqvp = dq->dq_vp) == NULLVP) panic("dqsync: file"); if (vp != dqvp) vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, p); while (dq->dq_flags & DQ_LOCK) { dq->dq_flags |= DQ_WANT; (void) tsleep(dq, PINOD+2, "dqsync", 0); if ((dq->dq_flags & DQ_MOD) == 0) { if (vp != dqvp) VOP_UNLOCK(dqvp, 0, p); return (0); } } dq->dq_flags |= DQ_LOCK; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; aiov.iov_base = (caddr_t)&dq->dq_dqb; aiov.iov_len = sizeof (struct dqblk); auio.uio_resid = sizeof (struct dqblk); auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk)); auio.uio_segflg = UIO_SYSSPACE; auio.uio_rw = UIO_WRITE; auio.uio_procp = (struct proc *)0; error = VOP_WRITE(dqvp, &auio, 0, dq->dq_cred); if (auio.uio_resid && error == 0) error = EIO; if (dq->dq_flags & DQ_WANT) wakeup(dq); dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT); if (vp != dqvp) VOP_UNLOCK(dqvp, 0, p); return (error); } int ufs_quota_delete(struct inode *ip) { struct vnode *vp = ITOV(ip); int i; for (i = 0; i < MAXQUOTAS; i++) { if (ip->i_dquot[i] != NODQUOT) { dqrele(vp, ip->i_dquot[i]); ip->i_dquot[i] = NODQUOT; } } return (0); } /* * Do operations associated with quotas */ int ufs_quotactl(mp, cmds, uid, arg, p) struct mount *mp; int cmds; uid_t uid; caddr_t arg; struct proc *p; { int cmd, type, error; if (uid == -1) uid = p->p_cred->p_ruid; cmd = cmds >> SUBCMDSHIFT; switch (cmd) { case Q_SYNC: break; case Q_GETQUOTA: if (uid == p->p_cred->p_ruid) break; /* fall through */ default: if ((error = suser(p, 0)) != 0) return (error); } type = cmds & SUBCMDMASK; if ((u_int)type >= MAXQUOTAS) return (EINVAL); if (vfs_busy(mp, LK_NOWAIT, 0, p)) return (0); switch (cmd) { case Q_QUOTAON: error = quotaon(p, mp, type, arg); break; case Q_QUOTAOFF: error = quotaoff(p, mp, type); break; case Q_SETQUOTA: error = setquota(mp, uid, type, arg) ; break; case Q_SETUSE: error = setuse(mp, uid, type, arg); break; case Q_GETQUOTA: error = getquota(mp, uid, type, arg); break; case Q_SYNC: error = qsync(mp); break; default: error = EINVAL; break; } vfs_unbusy(mp, p); return (error); } makefs/src/sys/ufs/ufs/ufs_quota_stub.c010064400000000000000000000024371020120120700154500ustar00/* $OpenBSD: ufs_quota_stub.c,v 1.3 2003/05/26 18:33:17 tedu Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef QUOTA int getinoquota(struct inode *ip) { return (0); } int ufs_quota_alloc_blocks2(struct inode *ip, daddr_t change, struct ucred *cred, enum ufs_quota_flags flags) { return (0); } int ufs_quota_free_blocks2(struct inode *ip, daddr_t change, struct ucred *cred, enum ufs_quota_flags flags) { return (0); } int ufs_quota_alloc_inode2(struct inode *ip, struct ucred *cred, enum ufs_quota_flags flags) { return (0); } int ufs_quota_free_inode2(struct inode *ip, struct ucred *cred, enum ufs_quota_flags flags) { return (0); } int quotaoff(struct proc *p, struct mount *mp, int flags) { return (0); } int qsync(struct mount *mp) { return (0); } int ufs_quotactl(struct mount *mp, int a, uid_t u, caddr_t addr, struct proc *p) { return (EOPNOTSUPP); } void ufs_quota_init(void) { } int ufs_quota_delete(struct inode *ip) { return (0); } #endif makefs/src/sys/ufs/ufs/ufs_readwrite.c010064400000000000000000000205771067402745200153030ustar00/* $OpenBSD: ufs_readwrite.c,v 1.26 2004/07/13 21:04:29 millert Exp $ */ /* $NetBSD: ufs_readwrite.c,v 1.9 1996/05/11 18:27:57 mycroft Exp $ */ /*- * Copyright (c) 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. * * @(#)ufs_readwrite.c 8.11 (Berkeley) 5/8/95 */ #ifdef LFS_READWRITE #define BLKSIZE(a, b, c) blksize(a, b, c) #define FS struct lfs #define I_FS i_lfs #define READ lfs_read #define READ_S "lfs_read" #define WRITE lfs_write #define WRITE_S "lfs_write" #define fs_bsize lfs_bsize #define MAXFILESIZE fs->lfs_maxfilesize #else #define BLKSIZE(a, b, c) blksize(a, b, c) #define FS struct fs #define I_FS i_fs #define READ ffs_read #define READ_S "ffs_read" #define WRITE ffs_write #define WRITE_S "ffs_write" #define MAXFILESIZE fs->fs_maxfilesize #endif #include #define VN_KNOTE(vp, b) \ KNOTE((struct klist *)&vp->v_selectinfo.vsi_selinfo.si_note, (b)) /* * Vnode op for reading. */ /* ARGSUSED */ int READ(v) void *v; { struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap = v; register struct vnode *vp; register struct inode *ip; register struct uio *uio; register FS *fs; struct buf *bp; daddr_t lbn, nextlbn; off_t bytesinfile; long size, xfersize, blkoffset; mode_t mode; int error; vp = ap->a_vp; ip = VTOI(vp); mode = ip->i_ffs_mode; uio = ap->a_uio; #ifdef DIAGNOSTIC if (uio->uio_rw != UIO_READ) panic("%s: mode", READ_S); if (vp->v_type == VLNK) { if ((int)ip->i_ffs_size < vp->v_mount->mnt_maxsymlinklen || (vp->v_mount->mnt_maxsymlinklen == 0 && ip->i_ffs_blocks == 0)) panic("%s: short symlink", READ_S); } else if (vp->v_type != VREG && vp->v_type != VDIR) panic("%s: type %d", READ_S, vp->v_type); #endif fs = ip->I_FS; if ((u_int64_t)uio->uio_offset > MAXFILESIZE) return (EFBIG); if (uio->uio_resid == 0) return (0); for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) { if ((bytesinfile = ip->i_ffs_size - uio->uio_offset) <= 0) break; lbn = lblkno(fs, uio->uio_offset); nextlbn = lbn + 1; size = BLKSIZE(fs, ip, lbn); blkoffset = blkoff(fs, uio->uio_offset); xfersize = fs->fs_bsize - blkoffset; if (uio->uio_resid < xfersize) xfersize = uio->uio_resid; if (bytesinfile < xfersize) xfersize = bytesinfile; #ifdef LFS_READWRITE (void)lfs_check(vp, lbn); error = cluster_read(vp, &ip->i_ci, ip->i_ffs_size, lbn, size, NOCRED, &bp); #else if (lblktosize(fs, nextlbn) >= ip->i_ffs_size) error = bread(vp, lbn, size, NOCRED, &bp); else if (doclusterread) error = cluster_read(vp, &ip->i_ci, ip->i_ffs_size, lbn, size, NOCRED, &bp); else if (lbn - 1 == ip->i_ci.ci_lastr) { int nextsize = BLKSIZE(fs, ip, nextlbn); error = breadn(vp, lbn, size, &nextlbn, &nextsize, 1, NOCRED, &bp); } else error = bread(vp, lbn, size, NOCRED, &bp); #endif if (error) break; ip->i_ci.ci_lastr = lbn; /* * We should only get non-zero b_resid when an I/O error * has occurred, which should cause us to break above. * However, if the short read did not cause an error, * then we want to ensure that we do not uiomove bad * or uninitialized data. */ size -= bp->b_resid; if (size < xfersize) { if (size == 0) break; xfersize = size; } error = uiomove((char *)bp->b_data + blkoffset, (int)xfersize, uio); if (error) break; brelse(bp); } if (bp != NULL) brelse(bp); ip->i_flag |= IN_ACCESS; return (error); } /* * Vnode op for writing. */ int WRITE(v) void *v; { struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap = v; register struct vnode *vp; register struct uio *uio; register struct inode *ip; register FS *fs; struct buf *bp; struct proc *p; daddr_t lbn; off_t osize; int blkoffset, error, extended, flags, ioflag, resid, size, xfersize; extended = 0; ioflag = ap->a_ioflag; uio = ap->a_uio; vp = ap->a_vp; ip = VTOI(vp); #ifdef DIAGNOSTIC if (uio->uio_rw != UIO_WRITE) panic("%s: mode", WRITE_S); #endif /* * If writing 0 bytes, succeed and do not change * update time or file offset (standards compliance) */ if (uio->uio_resid == 0) return (0); switch (vp->v_type) { case VREG: if (ioflag & IO_APPEND) uio->uio_offset = ip->i_ffs_size; if ((ip->i_ffs_flags & APPEND) && uio->uio_offset != ip->i_ffs_size) return (EPERM); /* FALLTHROUGH */ case VLNK: break; case VDIR: if ((ioflag & IO_SYNC) == 0) panic("%s: nonsync dir write", WRITE_S); break; default: panic("%s: type", WRITE_S); } fs = ip->I_FS; if (uio->uio_offset < 0 || (u_int64_t)uio->uio_offset + uio->uio_resid > MAXFILESIZE) return (EFBIG); /* * Maybe this should be above the vnode op call, but so long as * file servers have no limits, I don't think it matters. */ p = uio->uio_procp; if (vp->v_type == VREG && p && !(ioflag & IO_NOLIMIT) && uio->uio_offset + uio->uio_resid > p->p_rlimit[RLIMIT_FSIZE].rlim_cur) { psignal(p, SIGXFSZ); return (EFBIG); } resid = uio->uio_resid; osize = ip->i_ffs_size; flags = ioflag & IO_SYNC ? B_SYNC : 0; for (error = 0; uio->uio_resid > 0;) { lbn = lblkno(fs, uio->uio_offset); blkoffset = blkoff(fs, uio->uio_offset); xfersize = fs->fs_bsize - blkoffset; if (uio->uio_resid < xfersize) xfersize = uio->uio_resid; if (fs->fs_bsize > xfersize) flags |= B_CLRBUF; else flags &= ~B_CLRBUF; if ((error = UFS_BUF_ALLOC(ip, uio->uio_offset, xfersize, ap->a_cred, flags, &bp)) != 0) break; if (uio->uio_offset + xfersize > ip->i_ffs_size) { ip->i_ffs_size = uio->uio_offset + xfersize; uvm_vnp_setsize(vp, ip->i_ffs_size); extended = 1; } (void)uvm_vnp_uncache(vp); size = BLKSIZE(fs, ip, lbn) - bp->b_resid; if (size < xfersize) xfersize = size; error = uiomove((char *)bp->b_data + blkoffset, xfersize, uio); if (error != 0) bzero((char *)bp->b_data + blkoffset, xfersize); #ifdef LFS_READWRITE (void)VOP_BWRITE(bp); #else if (ioflag & IO_SYNC) (void)bwrite(bp); else if (xfersize + blkoffset == fs->fs_bsize) { if (doclusterwrite) cluster_write(bp, &ip->i_ci, ip->i_ffs_size); else bawrite(bp); } else bdwrite(bp); #endif if (error || xfersize == 0) break; ip->i_flag |= IN_CHANGE | IN_UPDATE; } /* * If we successfully wrote any data, and we are not the superuser * we clear the setuid and setgid bits as a precaution against * tampering. */ if (resid > uio->uio_resid && ap->a_cred && ap->a_cred->cr_uid != 0) ip->i_ffs_mode &= ~(ISUID | ISGID); if (resid > uio->uio_resid) VN_KNOTE(vp, NOTE_WRITE | (extended ? NOTE_EXTEND : 0)); if (error) { if (ioflag & IO_UNIT) { (void)UFS_TRUNCATE(ip, osize, ioflag & IO_SYNC, ap->a_cred); uio->uio_offset -= resid - uio->uio_resid; uio->uio_resid = resid; } } else if (resid > uio->uio_resid && (ioflag & IO_SYNC)) { error = UFS_UPDATE(ip, MNT_WAIT); } return (error); } makefs/src/sys/ufs/ufs/ufs_vfsops.c010064400000000000000000000103051020120120700145730ustar00/* $OpenBSD: ufs_vfsops.c,v 1.11 2003/12/28 17:20:16 tedu Exp $ */ /* $NetBSD: ufs_vfsops.c,v 1.4 1996/02/09 22:36:12 christos Exp $ */ /* * Copyright (c) 1991, 1993, 1994 * 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. * * @(#)ufs_vfsops.c 8.4 (Berkeley) 4/16/94 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef UFS_DIRHASH #include #include #endif /* * Make a filesystem operational. * Nothing to do at the moment. */ /* ARGSUSED */ int ufs_start(mp, flags, p) struct mount *mp; int flags; struct proc *p; { return (0); } /* * Return the root of a filesystem. */ int ufs_root(mp, vpp) struct mount *mp; struct vnode **vpp; { struct vnode *nvp; int error; if ((error = VFS_VGET(mp, (ino_t)ROOTINO, &nvp)) != 0) return (error); *vpp = nvp; return (0); } /* * Verify a remote client has export rights and return these rights via. * exflagsp and credanonp. */ int ufs_check_export(mp, nam, exflagsp, credanonp) register struct mount *mp; struct mbuf *nam; int *exflagsp; struct ucred **credanonp; { register struct netcred *np; register struct ufsmount *ump = VFSTOUFS(mp); /* * Get the export permission structure for this tuple. */ np = vfs_export_lookup(mp, &ump->um_export, nam); if (np == NULL) return (EACCES); *exflagsp = np->netc_exflags; *credanonp = &np->netc_anon; return (0); } /* * Initial UFS filesystems, done only once. */ int ufs_init(vfsp) struct vfsconf *vfsp; { static int done; if (done) return (0); done = 1; ufs_ihashinit(); ufs_quota_init(); #ifdef UFS_DIRHASH ufsdirhash_init(); #endif return (0); } /* * This is the generic part of fhtovp called after the underlying * filesystem has validated the file handle. */ int ufs_fhtovp(mp, ufhp, vpp) register struct mount *mp; struct ufid *ufhp; struct vnode **vpp; { register struct inode *ip; struct vnode *nvp; int error; if ((error = VFS_VGET(mp, ufhp->ufid_ino, &nvp)) != 0) { *vpp = NULLVP; return (error); } ip = VTOI(nvp); if (ip->i_ffs_mode == 0 || ip->i_ffs_gen != ufhp->ufid_gen) { vput(nvp); *vpp = NULLVP; return (ESTALE); } *vpp = nvp; return (0); } makefs/src/sys/ufs/ufs/ufs_vnops.c010064400000000000000000001464521341414421700144550ustar00/* $OpenBSD: ufs_vnops.c,v 1.67 2005/07/24 05:43:36 millert Exp $ */ /* $NetBSD: ufs_vnops.c,v 1.18 1996/05/11 18:28:04 mycroft Exp $ */ /* * Copyright (c) 1982, 1986, 1989, 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. * * @(#)ufs_vnops.c 8.14 (Berkeley) 10/26/94 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __GLIBC__ #include #endif #include #include #include #include #include #include #include #include #ifdef UFS_DIRHASH #include #endif #include static int ufs_chmod(struct vnode *, int, struct ucred *, struct proc *); static int ufs_chown(struct vnode *, uid_t, gid_t, struct ucred *, struct proc *); int filt_ufsread(struct knote *kn, long hint); int filt_ufswrite(struct knote *kn, long hint); int filt_ufsvnode(struct knote *kn, long hint); void filt_ufsdetach(struct knote *kn); union _qcvt { int64_t qcvt; int32_t val[2]; }; #define SETHIGH(q, h) { \ union _qcvt tmp; \ tmp.qcvt = (q); \ tmp.val[_QUAD_HIGHWORD] = (h); \ (q) = tmp.qcvt; \ } #define SETLOW(q, l) { \ union _qcvt tmp; \ tmp.qcvt = (q); \ tmp.val[_QUAD_LOWWORD] = (l); \ (q) = tmp.qcvt; \ } #define VN_KNOTE(vp, b) \ KNOTE(&vp->v_selectinfo.vsi_selinfo.si_note, (b)) /* * A virgin directory (no blushing please). */ static struct dirtemplate mastertemplate = { 0, 12, DT_DIR, 1, ".", 0, DIRBLKSIZ - 12, DT_DIR, 2, ".." }; static struct odirtemplate omastertemplate = { 0, 12, 1, ".", 0, DIRBLKSIZ - 12, 2, ".." }; /* * Create a regular file */ int ufs_create(v) void *v; { struct vop_create_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap = v; int error; error = ufs_makeinode(MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode), ap->a_dvp, ap->a_vpp, ap->a_cnp); if (error) return (error); VN_KNOTE(ap->a_dvp, NOTE_WRITE); return (0); } /* * Mknod vnode call */ /* ARGSUSED */ int ufs_mknod(v) void *v; { struct vop_mknod_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap = v; struct vattr *vap = ap->a_vap; struct vnode **vpp = ap->a_vpp; struct inode *ip; int error; if ((error = ufs_makeinode(MAKEIMODE(vap->va_type, vap->va_mode), ap->a_dvp, vpp, ap->a_cnp)) != 0) return (error); VN_KNOTE(ap->a_dvp, NOTE_WRITE); ip = VTOI(*vpp); ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE; if (vap->va_rdev != VNOVAL) { /* * Want to be able to use this to make badblock * inodes, so don't truncate the dev number. */ ip->i_ffs_rdev = vap->va_rdev; } /* * Remove inode so that it will be reloaded by VFS_VGET and * checked to see if it is an alias of an existing entry in * the inode cache. */ vput(*vpp); (*vpp)->v_type = VNON; vgone(*vpp); *vpp = 0; return (0); } /* * Open called. * * Nothing to do. */ /* ARGSUSED */ int ufs_open(v) void *v; { struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap = v; struct inode *ip = VTOI(ap->a_vp); /* * Files marked append-only must be opened for appending. */ if ((ip->i_ffs_flags & APPEND) && (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE) return (EPERM); if (ap->a_mode & O_TRUNC) ip->i_flag |= IN_CHANGE | IN_UPDATE; return (0); } /* * Close called. * * Update the times on the inode. */ /* ARGSUSED */ int ufs_close(v) void *v; { struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap = v; struct vnode *vp = ap->a_vp; struct inode *ip = VTOI(vp); simple_lock(&vp->v_interlock); if (vp->v_usecount > 1) ITIMES(ip, &time, &time); simple_unlock(&vp->v_interlock); return (0); } int ufs_access(v) void *v; { struct vop_access_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap = v; struct vnode *vp = ap->a_vp; struct inode *ip = VTOI(vp); mode_t mode = ap->a_mode; /* * Disallow write attempts on read-only filesystems; * unless the file is a socket, fifo, or a block or * character device resident on the filesystem. */ if (mode & VWRITE) { switch (vp->v_type) { int error; case VDIR: case VLNK: case VREG: if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if ((error = getinoquota(ip)) != 0) return (error); break; case VBAD: case VBLK: case VCHR: case VSOCK: case VFIFO: case VNON: break; } } /* If immutable bit set, nobody gets to write it. */ if ((mode & VWRITE) && (ip->i_ffs_flags & IMMUTABLE)) return (EPERM); return (vaccess(ip->i_ffs_mode, ip->i_ffs_uid, ip->i_ffs_gid, mode, ap->a_cred)); } /* ARGSUSED */ int ufs_getattr(v) void *v; { struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap = v; struct vnode *vp = ap->a_vp; struct inode *ip = VTOI(vp); struct vattr *vap = ap->a_vap; ITIMES(ip, &time, &time); /* * Copy from inode table */ vap->va_fsid = ip->i_dev; vap->va_fileid = ip->i_number; vap->va_mode = ip->i_ffs_mode & ~IFMT; vap->va_nlink = ip->i_effnlink; vap->va_uid = ip->i_ffs_uid; vap->va_gid = ip->i_ffs_gid; vap->va_rdev = (dev_t)ip->i_ffs_rdev; vap->va_size = ip->i_ffs_size; vap->va_atime.tv_sec = ip->i_ffs_atime; vap->va_atime.tv_nsec = ip->i_ffs_atimensec; vap->va_mtime.tv_sec = ip->i_ffs_mtime; vap->va_mtime.tv_nsec = ip->i_ffs_mtimensec; vap->va_ctime.tv_sec = ip->i_ffs_ctime; vap->va_ctime.tv_nsec = ip->i_ffs_ctimensec; vap->va_flags = ip->i_ffs_flags; vap->va_gen = ip->i_ffs_gen; /* this doesn't belong here */ if (vp->v_type == VBLK) vap->va_blocksize = BLKDEV_IOSIZE; else if (vp->v_type == VCHR) vap->va_blocksize = MAXBSIZE; else vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize; vap->va_bytes = dbtob((u_quad_t)ip->i_ffs_blocks); vap->va_type = vp->v_type; vap->va_filerev = ip->i_modrev; return (0); } /* * Set attribute vnode op. called from several syscalls */ int ufs_setattr(v) void *v; { struct vop_setattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap = v; struct vattr *vap = ap->a_vap; struct vnode *vp = ap->a_vp; struct inode *ip = VTOI(vp); struct ucred *cred = ap->a_cred; struct proc *p = ap->a_p; int error; long hint = NOTE_ATTRIB; u_quad_t oldsize; /* * Check for unsettable attributes. */ if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) || (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) || (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) || ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) { return (EINVAL); } if (vap->va_flags != VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if (cred->cr_uid != ip->i_ffs_uid && (error = suser_ucred(cred))) return (error); if (cred->cr_uid == 0) { if ((ip->i_ffs_flags & (SF_IMMUTABLE | SF_APPEND)) && securelevel > 0) return (EPERM); ip->i_ffs_flags = vap->va_flags; } else { if (ip->i_ffs_flags & (SF_IMMUTABLE | SF_APPEND) || (vap->va_flags & UF_SETTABLE) != vap->va_flags) return (EPERM); ip->i_ffs_flags &= SF_SETTABLE; ip->i_ffs_flags |= (vap->va_flags & UF_SETTABLE); } ip->i_flag |= IN_CHANGE; if (vap->va_flags & (IMMUTABLE | APPEND)) return (0); } if (ip->i_ffs_flags & (IMMUTABLE | APPEND)) return (EPERM); /* * Go through the fields and update if not VNOVAL. */ if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); error = ufs_chown(vp, vap->va_uid, vap->va_gid, cred, p); if (error) return (error); } if (vap->va_size != VNOVAL) { oldsize = ip->i_ffs_size; /* * Disallow write attempts on read-only filesystems; * unless the file is a socket, fifo, or a block or * character device resident on the filesystem. */ switch (vp->v_type) { case VDIR: return (EISDIR); case VLNK: case VREG: if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); break; default: break; } if ((error = UFS_TRUNCATE(ip, vap->va_size, 0, cred)) != 0) return (error); if (vap->va_size < oldsize) hint |= NOTE_TRUNCATE; } if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if (cred->cr_uid != ip->i_ffs_uid && (error = suser_ucred(cred)) && ((vap->va_vaflags & VA_UTIMES_NULL) == 0 || (error = VOP_ACCESS(vp, VWRITE, cred, p)))) return (error); if (vap->va_atime.tv_sec != VNOVAL) ip->i_flag |= IN_ACCESS; if (vap->va_mtime.tv_sec != VNOVAL) ip->i_flag |= IN_CHANGE | IN_UPDATE; error = UFS_UPDATE2(ip, &vap->va_atime, &vap->va_mtime, 0); if (error) return (error); } error = 0; if (vap->va_mode != (mode_t)VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); error = ufs_chmod(vp, (int)vap->va_mode, cred, p); } VN_KNOTE(vp, hint); return (error); } /* * Change the mode on a file. * Inode must be locked before calling. */ static int ufs_chmod(vp, mode, cred, p) struct vnode *vp; int mode; struct ucred *cred; struct proc *p; { struct inode *ip = VTOI(vp); int error; if (cred->cr_uid != ip->i_ffs_uid && (error = suser_ucred(cred))) return (error); if (cred->cr_uid) { if (vp->v_type != VDIR && (mode & S_ISTXT)) return (EFTYPE); if (!groupmember(ip->i_ffs_gid, cred) && (mode & ISGID)) return (EPERM); } ip->i_ffs_mode &= ~ALLPERMS; ip->i_ffs_mode |= (mode & ALLPERMS); ip->i_flag |= IN_CHANGE; if ((vp->v_flag & VTEXT) && (ip->i_ffs_mode & S_ISTXT) == 0) (void) uvm_vnp_uncache(vp); return (0); } /* * Perform chown operation on inode ip; * inode must be locked prior to call. */ static int ufs_chown(vp, uid, gid, cred, p) struct vnode *vp; uid_t uid; gid_t gid; struct ucred *cred; struct proc *p; { struct inode *ip = VTOI(vp); uid_t ouid; gid_t ogid; int error = 0; daddr_t change; enum ufs_quota_flags quota_flags = 0; if (uid == (uid_t)VNOVAL) uid = ip->i_ffs_uid; if (gid == (gid_t)VNOVAL) gid = ip->i_ffs_gid; /* * If we don't own the file, are trying to change the owner * of the file, or are not a member of the target group, * the caller must be superuser or the call fails. */ if ((cred->cr_uid != ip->i_ffs_uid || uid != ip->i_ffs_uid || (gid != ip->i_ffs_gid && !groupmember((gid_t)gid, cred))) && (error = suser_ucred(cred))) return (error); ogid = ip->i_ffs_gid; ouid = ip->i_ffs_uid; change = ip->i_ffs_blocks; if (ouid == uid) quota_flags |= UFS_QUOTA_NOUID; if (ogid == gid) quota_flags |= UFS_QUOTA_NOGID; if ((error = getinoquota(ip)) != 0) return (error); (void) ufs_quota_free_blocks2(ip, change, cred, quota_flags); (void) ufs_quota_free_inode2(ip, cred, quota_flags); (void) ufs_quota_delete(ip); ip->i_ffs_gid = gid; ip->i_ffs_uid = uid; if ((error = getinoquota(ip)) != 0) goto error; if ((error = ufs_quota_alloc_blocks2(ip, change, cred, quota_flags)) != 0) goto error; if ((error = ufs_quota_alloc_inode2(ip, cred , quota_flags)) != 0) { (void)ufs_quota_free_blocks2(ip, change, cred, quota_flags); goto error; } if (getinoquota(ip)) panic("chown: lost quota"); if (ouid != uid || ogid != gid) ip->i_flag |= IN_CHANGE; if (ouid != uid && cred->cr_uid != 0) ip->i_ffs_mode &= ~ISUID; if (ogid != gid && cred->cr_uid != 0) ip->i_ffs_mode &= ~ISGID; return (0); error: (void) ufs_quota_delete(ip); ip->i_ffs_gid = ogid; ip->i_ffs_uid = ouid; if (getinoquota(ip) == 0) { (void) ufs_quota_alloc_blocks2(ip, change, cred, quota_flags | UFS_QUOTA_FORCE); (void) ufs_quota_alloc_inode2(ip, cred, quota_flags | UFS_QUOTA_FORCE); (void) getinoquota(ip); } return (error); } /* ARGSUSED */ int ufs_ioctl(v) void *v; { #if 0 struct vop_ioctl_args /* { struct vnode *a_vp; u_long a_command; caddr_t a_data; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap = v; #endif return (ENOTTY); } /* ARGSUSED */ int ufs_poll(v) void *v; { struct vop_poll_args /* { struct vnode *a_vp; int a_events; struct proc *a_p; } */ *ap = v; /* * We should really check to see if I/O is possible. */ return (ap->a_events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)); } /* * Seek on a file * * Nothing to do, so just return. */ /* ARGSUSED */ int ufs_seek(v) void *v; { #if 0 struct vop_seek_args /* { struct vnode *a_vp; off_t a_oldoff; off_t a_newoff; struct ucred *a_cred; } */ *ap = v; #endif return (0); } int ufs_remove(v) void *v; { struct vop_remove_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap = v; struct inode *ip; struct vnode *vp = ap->a_vp; struct vnode *dvp = ap->a_dvp; int error; ip = VTOI(vp); if (vp->v_type == VDIR || (ip->i_ffs_flags & (IMMUTABLE | APPEND)) || (VTOI(dvp)->i_ffs_flags & APPEND)) { error = EPERM; goto out; } error = ufs_dirremove(dvp, ip, ap->a_cnp->cn_flags, 0); VN_KNOTE(vp, NOTE_DELETE); VN_KNOTE(dvp, NOTE_WRITE); out: if (dvp == vp) vrele(vp); else vput(vp); vput(dvp); return (error); } /* * link vnode call */ int ufs_link(v) void *v; { struct vop_link_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap = v; struct vnode *dvp = ap->a_dvp; struct vnode *vp = ap->a_vp; struct componentname *cnp = ap->a_cnp; struct proc *p = cnp->cn_proc; struct inode *ip; struct direct newdir; int error; #ifdef DIAGNOSTIC if ((cnp->cn_flags & HASBUF) == 0) panic("ufs_link: no name"); #endif if (vp->v_type == VDIR) { VOP_ABORTOP(dvp, cnp); error = EPERM; goto out2; } if (dvp->v_mount != vp->v_mount) { VOP_ABORTOP(dvp, cnp); error = EXDEV; goto out2; } if (dvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE, p))) { VOP_ABORTOP(dvp, cnp); goto out2; } ip = VTOI(vp); if ((nlink_t)ip->i_ffs_nlink >= LINK_MAX) { VOP_ABORTOP(dvp, cnp); error = EMLINK; goto out1; } if (ip->i_ffs_flags & (IMMUTABLE | APPEND)) { VOP_ABORTOP(dvp, cnp); error = EPERM; goto out1; } ip->i_effnlink++; ip->i_ffs_nlink++; ip->i_flag |= IN_CHANGE; if (DOINGSOFTDEP(vp)) softdep_change_linkcnt(ip, 0); if ((error = UFS_UPDATE(ip, !DOINGSOFTDEP(vp))) == 0) { ufs_makedirentry(ip, cnp, &newdir); error = ufs_direnter(dvp, vp, &newdir, cnp, NULL); } if (error) { ip->i_effnlink--; ip->i_ffs_nlink--; ip->i_flag |= IN_CHANGE; if (DOINGSOFTDEP(vp)) softdep_change_linkcnt(ip, 0); } pool_put(&namei_pool, cnp->cn_pnbuf); VN_KNOTE(vp, NOTE_LINK); VN_KNOTE(dvp, NOTE_WRITE); out1: if (dvp != vp) VOP_UNLOCK(vp, 0, p); out2: vput(dvp); return (error); } /* * Rename system call. * rename("foo", "bar"); * is essentially * unlink("bar"); * link("foo", "bar"); * unlink("foo"); * but ``atomically''. Can't do full commit without saving state in the * inode on disk which isn't feasible at this time. Best we can do is * always guarantee the target exists. * * Basic algorithm is: * * 1) Bump link count on source while we're linking it to the * target. This also ensure the inode won't be deleted out * from underneath us while we work (it may be truncated by * a concurrent `trunc' or `open' for creation). * 2) Link source to destination. If destination already exists, * delete it first. * 3) Unlink source reference to inode if still around. If a * directory was moved and the parent of the destination * is different from the source, patch the ".." entry in the * directory. */ int ufs_rename(v) void *v; { struct vop_rename_args /* { struct vnode *a_fdvp; struct vnode *a_fvp; struct componentname *a_fcnp; struct vnode *a_tdvp; struct vnode *a_tvp; struct componentname *a_tcnp; } */ *ap = v; struct vnode *tvp = ap->a_tvp; struct vnode *tdvp = ap->a_tdvp; struct vnode *fvp = ap->a_fvp; struct vnode *fdvp = ap->a_fdvp; struct componentname *tcnp = ap->a_tcnp; struct componentname *fcnp = ap->a_fcnp; struct proc *p = fcnp->cn_proc; struct inode *ip, *xp, *dp; struct direct newdir; int doingdirectory = 0, oldparent = 0, newparent = 0; int error = 0; #ifdef DIAGNOSTIC if ((tcnp->cn_flags & HASBUF) == 0 || (fcnp->cn_flags & HASBUF) == 0) panic("ufs_rename: no name"); #endif /* * Check for cross-device rename. */ if ((fvp->v_mount != tdvp->v_mount) || (tvp && (fvp->v_mount != tvp->v_mount))) { error = EXDEV; abortit: VOP_ABORTOP(tdvp, tcnp); /* XXX, why not in NFS? */ if (tdvp == tvp) vrele(tdvp); else vput(tdvp); if (tvp) vput(tvp); VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */ vrele(fdvp); vrele(fvp); return (error); } if (tvp && ((VTOI(tvp)->i_ffs_flags & (IMMUTABLE | APPEND)) || (VTOI(tdvp)->i_ffs_flags & APPEND))) { error = EPERM; goto abortit; } /* * Check if just deleting a link name or if we've lost a race. * If another process completes the same rename after we've looked * up the source and have blocked looking up the target, then the * source and target inodes may be identical now although the * names were never linked. */ if (fvp == tvp) { if (fvp->v_type == VDIR) { /* * Linked directories are impossible, so we must * have lost the race. Pretend that the rename * completed before the lookup. */ error = ENOENT; goto abortit; } /* Release destination completely. */ VOP_ABORTOP(tdvp, tcnp); vput(tdvp); vput(tvp); /* * Delete source. There is another race now that everything * is unlocked, but this doesn't cause any new complications. * relookup() may find a file that is unrelated to the * original one, or it may fail. Too bad. */ vrele(fvp); fcnp->cn_flags &= ~MODMASK; fcnp->cn_flags |= LOCKPARENT | LOCKLEAF; if ((fcnp->cn_flags & SAVESTART) == 0) panic("ufs_rename: lost from startdir"); fcnp->cn_nameiop = DELETE; if ((error = relookup(fdvp, &fvp, fcnp)) != 0) return (error); /* relookup did vrele() */ vrele(fdvp); return (VOP_REMOVE(fdvp, fvp, fcnp)); } if ((error = vn_lock(fvp, LK_EXCLUSIVE, p)) != 0) goto abortit; /* fvp, tdvp, tvp now locked */ dp = VTOI(fdvp); ip = VTOI(fvp); if ((nlink_t)ip->i_ffs_nlink >= LINK_MAX) { VOP_UNLOCK(fvp, 0, p); error = EMLINK; goto abortit; } if ((ip->i_ffs_flags & (IMMUTABLE | APPEND)) || (dp->i_ffs_flags & APPEND)) { VOP_UNLOCK(fvp, 0, p); error = EPERM; goto abortit; } if ((ip->i_ffs_mode & IFMT) == IFDIR) { error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc); if (!error && tvp) error = VOP_ACCESS(tvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc); if (error) { VOP_UNLOCK(fvp, 0, p); error = EACCES; goto abortit; } /* * Avoid ".", "..", and aliases of "." for obvious reasons. */ if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') || dp == ip || (fcnp->cn_flags & ISDOTDOT) || (tcnp->cn_flags & ISDOTDOT) || (ip->i_flag & IN_RENAME)) { VOP_UNLOCK(fvp, 0, p); error = EINVAL; goto abortit; } ip->i_flag |= IN_RENAME; oldparent = dp->i_number; doingdirectory = 1; } VN_KNOTE(fdvp, NOTE_WRITE); /* XXX right place? */ /* * When the target exists, both the directory * and target vnodes are returned locked. */ dp = VTOI(tdvp); xp = NULL; if (tvp) xp = VTOI(tvp); /* * 1) Bump link count while we're moving stuff * around. If we crash somewhere before * completing our work, the link count * may be wrong, but correctable. */ ip->i_effnlink++; ip->i_ffs_nlink++; ip->i_flag |= IN_CHANGE; if (DOINGSOFTDEP(fvp)) softdep_change_linkcnt(ip, 0); if ((error = UFS_UPDATE(ip, !DOINGSOFTDEP(fvp))) != 0) { VOP_UNLOCK(fvp, 0, p); goto bad; } /* * If ".." must be changed (ie the directory gets a new * parent) then the source directory must not be in the * directory hierarchy above the target, as this would * orphan everything below the source directory. Also * the user must have write permission in the source so * as to be able to change "..". We must repeat the call * to namei, as the parent directory is unlocked by the * call to checkpath(). */ error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc); VOP_UNLOCK(fvp, 0, p); /* tdvp and tvp locked */ if (oldparent != dp->i_number) newparent = dp->i_number; if (doingdirectory && newparent) { if (error) /* write access check above */ goto bad; if (xp != NULL) vput(tvp); /* * Compensate for the reference ufs_checkpath() loses. */ vref(tdvp); /* Only tdvp is locked */ if ((error = ufs_checkpath(ip, dp, tcnp->cn_cred)) != 0) goto out; if ((tcnp->cn_flags & SAVESTART) == 0) panic("ufs_rename: lost to startdir"); if ((error = relookup(tdvp, &tvp, tcnp)) != 0) goto out; dp = VTOI(tdvp); xp = NULL; if (tvp) xp = VTOI(tvp); } /* * 2) If target doesn't exist, link the target * to the source and unlink the source. * Otherwise, rewrite the target directory * entry to reference the source inode and * expunge the original entry's existence. */ if (xp == NULL) { if (dp->i_dev != ip->i_dev) panic("rename: EXDEV"); /* * Account for ".." in new directory. * When source and destination have the same * parent we don't fool with the link count. */ if (doingdirectory && newparent) { if ((nlink_t)dp->i_ffs_nlink >= LINK_MAX) { error = EMLINK; goto bad; } dp->i_effnlink++; dp->i_ffs_nlink++; dp->i_flag |= IN_CHANGE; if (DOINGSOFTDEP(tdvp)) softdep_change_linkcnt(dp, 0); if ((error = UFS_UPDATE(dp, !DOINGSOFTDEP(tdvp))) != 0) { dp->i_effnlink--; dp->i_ffs_nlink--; dp->i_flag |= IN_CHANGE; if (DOINGSOFTDEP(tdvp)) softdep_change_linkcnt(dp, 0); goto bad; } } ufs_makedirentry(ip, tcnp, &newdir); if ((error = ufs_direnter(tdvp, NULL, &newdir, tcnp, NULL)) != 0) { if (doingdirectory && newparent) { dp->i_effnlink--; dp->i_ffs_nlink--; dp->i_flag |= IN_CHANGE; if (DOINGSOFTDEP(tdvp)) softdep_change_linkcnt(dp, 0); (void)UFS_UPDATE(dp, 1); } goto bad; } VN_KNOTE(tdvp, NOTE_WRITE); vput(tdvp); } else { if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev) panic("rename: EXDEV"); /* * Short circuit rename(foo, foo). */ if (xp->i_number == ip->i_number) panic("ufs_rename: same file"); /* * If the parent directory is "sticky", then the user must * own the parent directory, or the destination of the rename, * otherwise the destination may not be changed (except by * root). This implements append-only directories. */ if ((dp->i_ffs_mode & S_ISTXT) && tcnp->cn_cred->cr_uid != 0 && tcnp->cn_cred->cr_uid != dp->i_ffs_uid && xp->i_ffs_uid != tcnp->cn_cred->cr_uid) { error = EPERM; goto bad; } /* * Target must be empty if a directory and have no links * to it. Also, ensure source and target are compatible * (both directories, or both not directories). */ if ((xp->i_ffs_mode & IFMT) == IFDIR) { if (xp->i_effnlink > 2 || !ufs_dirempty(xp, dp->i_number, tcnp->cn_cred)) { error = ENOTEMPTY; goto bad; } if (!doingdirectory) { error = ENOTDIR; goto bad; } cache_purge(tdvp); } else if (doingdirectory) { error = EISDIR; goto bad; } if ((error = ufs_dirrewrite(dp, xp, ip->i_number, IFTODT(ip->i_ffs_mode), (doingdirectory && newparent) ? newparent : doingdirectory)) != 0) goto bad; if (doingdirectory) { if (!newparent) { dp->i_effnlink--; if (DOINGSOFTDEP(tdvp)) softdep_change_linkcnt(dp, 0); } xp->i_effnlink--; if (DOINGSOFTDEP(tvp)) softdep_change_linkcnt(xp, 0); } if (doingdirectory && !DOINGSOFTDEP(tvp)) { /* * Truncate inode. The only stuff left in the directory * is "." and "..". The "." reference is inconsequential * since we are quashing it. We have removed the "." * reference and the reference in the parent directory, * but there may be other hard links. The soft * dependency code will arrange to do these operations * after the parent directory entry has been deleted on * disk, so when running with that code we avoid doing * them now. */ if (!newparent) { dp->i_ffs_nlink--; dp->i_flag |= IN_CHANGE; } xp->i_ffs_nlink--; xp->i_flag |= IN_CHANGE; if ((error = UFS_TRUNCATE(VTOI(tvp), (off_t)0, IO_SYNC, tcnp->cn_cred)) != 0) goto bad; } VN_KNOTE(tdvp, NOTE_WRITE); vput(tdvp); VN_KNOTE(tvp, NOTE_DELETE); vput(tvp); xp = NULL; } /* * 3) Unlink the source. */ fcnp->cn_flags &= ~MODMASK; fcnp->cn_flags |= LOCKPARENT | LOCKLEAF; if ((fcnp->cn_flags & SAVESTART) == 0) panic("ufs_rename: lost from startdir"); if ((error = relookup(fdvp, &fvp, fcnp)) != 0) { vrele(ap->a_fvp); return (error); } vrele(fdvp); if (fvp == NULL) { /* * From name has disappeared. */ if (doingdirectory) panic("ufs_rename: lost dir entry"); vrele(ap->a_fvp); return (0); } xp = VTOI(fvp); dp = VTOI(fdvp); /* * Ensure that the directory entry still exists and has not * changed while the new name has been entered. If the source is * a file then the entry may have been unlinked or renamed. In * either case there is no further work to be done. If the source * is a directory then it cannot have been rmdir'ed; the IN_RENAME * flag ensures that it cannot be moved by another rename or removed * by a rmdir. */ if (xp != ip) { if (doingdirectory) panic("ufs_rename: lost dir entry"); } else { /* * If the source is a directory with a * new parent, the link count of the old * parent directory must be decremented * and ".." set to point to the new parent. */ if (doingdirectory && newparent) { xp->i_offset = mastertemplate.dot_reclen; ufs_dirrewrite(xp, dp, newparent, DT_DIR, 0); cache_purge(fdvp); } error = ufs_dirremove(fdvp, xp, fcnp->cn_flags, 0); xp->i_flag &= ~IN_RENAME; } VN_KNOTE(fvp, NOTE_RENAME); if (dp) vput(fdvp); if (xp) vput(fvp); vrele(ap->a_fvp); return (error); bad: if (xp) vput(ITOV(xp)); vput(ITOV(dp)); out: vrele(fdvp); if (doingdirectory) ip->i_flag &= ~IN_RENAME; if (vn_lock(fvp, LK_EXCLUSIVE, p) == 0) { ip->i_effnlink--; ip->i_ffs_nlink--; ip->i_flag |= IN_CHANGE; ip->i_flag &= ~IN_RENAME; if (DOINGSOFTDEP(fvp)) softdep_change_linkcnt(ip, 0); vput(fvp); } else vrele(fvp); return (error); } /* * Mkdir system call */ int ufs_mkdir(v) void *v; { struct vop_mkdir_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap = v; struct vnode *dvp = ap->a_dvp; struct vattr *vap = ap->a_vap; struct componentname *cnp = ap->a_cnp; struct inode *ip, *dp; struct vnode *tvp; struct buf *bp; struct direct newdir; struct dirtemplate dirtemplate, *dtp; int error, dmode, blkoff; #ifdef DIAGNOSTIC if ((cnp->cn_flags & HASBUF) == 0) panic("ufs_mkdir: no name"); #endif dp = VTOI(dvp); if ((nlink_t)dp->i_ffs_nlink >= LINK_MAX) { error = EMLINK; goto out; } dmode = vap->va_mode & 0777; dmode |= IFDIR; /* * Must simulate part of ufs_makeinode here to acquire the inode, * but not have it entered in the parent directory. The entry is * made later after writing "." and ".." entries. */ if ((error = UFS_INODE_ALLOC(dp, dmode, cnp->cn_cred, &tvp)) != 0) goto out; ip = VTOI(tvp); ip->i_ffs_uid = cnp->cn_cred->cr_uid; ip->i_ffs_gid = dp->i_ffs_gid; if ((error = getinoquota(ip)) || (error = ufs_quota_alloc_inode(ip, cnp->cn_cred))) { pool_put(&namei_pool, cnp->cn_pnbuf); UFS_INODE_FREE(ip, ip->i_number, dmode); vput(tvp); vput(dvp); return (error); } ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE; ip->i_ffs_mode = dmode; tvp->v_type = VDIR; /* Rest init'd in getnewvnode(). */ ip->i_effnlink = 2; ip->i_ffs_nlink = 2; if (DOINGSOFTDEP(tvp)) softdep_change_linkcnt(ip, 0); /* * Bump link count in parent directory to reflect work done below. * Should be done before reference is create so cleanup is * possible if we crash. */ dp->i_effnlink++; dp->i_ffs_nlink++; dp->i_flag |= IN_CHANGE; if (DOINGSOFTDEP(dvp)) softdep_change_linkcnt(dp, 0); if ((error = UFS_UPDATE(dp, !DOINGSOFTDEP(dvp))) != 0) goto bad; /* * Initialize directory with "." and ".." from static template. */ if (dvp->v_mount->mnt_maxsymlinklen > 0) dtp = &mastertemplate; else dtp = (struct dirtemplate *)&omastertemplate; dirtemplate = *dtp; dirtemplate.dot_ino = ip->i_number; dirtemplate.dotdot_ino = dp->i_number; if ((error = UFS_BUF_ALLOC(ip, (off_t)0, DIRBLKSIZ, cnp->cn_cred, B_CLRBUF, &bp)) != 0) goto bad; ip->i_ffs_size = DIRBLKSIZ; ip->i_flag |= IN_CHANGE | IN_UPDATE; uvm_vnp_setsize(tvp, ip->i_ffs_size); bcopy((caddr_t)&dirtemplate, (caddr_t)bp->b_data, sizeof dirtemplate); if (DOINGSOFTDEP(tvp)) { /* * Ensure that the entire newly allocated block is a * valid directory so that future growth within the * block does not have to ensure that the block is * written before the inode */ blkoff = DIRBLKSIZ; while (blkoff < bp->b_bcount) { ((struct direct *) (bp->b_data + blkoff))->d_reclen = DIRBLKSIZ; blkoff += DIRBLKSIZ; } } if ((error = UFS_UPDATE(ip, !DOINGSOFTDEP(tvp))) != 0) { (void)VOP_BWRITE(bp); goto bad; } /* * Directory set up, now install it's entry in the parent directory. * * If we are not doing soft dependencies, then we must write out the * buffer containing the new directory body before entering the new * name in the parent. If we are doing soft dependencies, then the * buffer containing the new directory body will be passed to and * released in the soft dependency code after the code has attached * an appropriate ordering dependency to the buffer which ensures that * the buffer is written before the new name is written in the parent. */ if (!DOINGSOFTDEP(dvp) && ((error = VOP_BWRITE(bp)) != 0)) goto bad; ufs_makedirentry(ip, cnp, &newdir); error = ufs_direnter(dvp, tvp, &newdir, cnp, bp); bad: if (error == 0) { VN_KNOTE(dvp, NOTE_WRITE); *ap->a_vpp = tvp; } else { dp->i_effnlink--; dp->i_ffs_nlink--; dp->i_flag |= IN_CHANGE; if (DOINGSOFTDEP(dvp)) softdep_change_linkcnt(dp, 0); /* * No need to do an explicit VOP_TRUNCATE here, vrele will * do this for us because we set the link count to 0. */ ip->i_effnlink = 0; ip->i_ffs_nlink = 0; ip->i_flag |= IN_CHANGE; if (DOINGSOFTDEP(tvp)) softdep_change_linkcnt(ip, 0); vput(tvp); } out: pool_put(&namei_pool, cnp->cn_pnbuf); vput(dvp); return (error); } /* * Rmdir system call. */ int ufs_rmdir(v) void *v; { struct vop_rmdir_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap = v; struct vnode *vp = ap->a_vp; struct vnode *dvp = ap->a_dvp; struct componentname *cnp = ap->a_cnp; struct inode *ip, *dp; int error; ip = VTOI(vp); dp = VTOI(dvp); /* * No rmdir "." or of mounted on directories. */ if (dp == ip || vp->v_mountedhere != 0) { if (dp == ip) vrele(dvp); else vput(dvp); vput(vp); return (EINVAL); } /* * Do not remove a directory that is in the process of being renamed. * Verify the directory is empty (and valid). Rmdir ".." will not be * valid since ".." will contain a reference to the current directory * and thus be non-empty. */ error = 0; if (ip->i_flag & IN_RENAME) { error = EINVAL; goto out; } if (ip->i_effnlink != 2 || !ufs_dirempty(ip, dp->i_number, cnp->cn_cred)) { error = ENOTEMPTY; goto out; } if ((dp->i_ffs_flags & APPEND) || (ip->i_ffs_flags & (IMMUTABLE | APPEND))) { error = EPERM; goto out; } /* * Delete reference to directory before purging * inode. If we crash in between, the directory * will be reattached to lost+found, */ dp->i_effnlink--; ip->i_effnlink--; if (DOINGSOFTDEP(vp)) { softdep_change_linkcnt(dp, 0); softdep_change_linkcnt(ip, 0); } if ((error = ufs_dirremove(dvp, ip, cnp->cn_flags, 1)) != 0) { dp->i_effnlink++; ip->i_effnlink++; if (DOINGSOFTDEP(vp)) { softdep_change_linkcnt(dp, 0); softdep_change_linkcnt(ip, 0); } goto out; } VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK); cache_purge(dvp); /* * Truncate inode. The only stuff left in the directory is "." and * "..". The "." reference is inconsequential since we are quashing * it. The soft dependency code will arrange to do these operations * after the parent directory entry has been deleted on disk, so * when running with that code we avoid doing them now. */ if (!DOINGSOFTDEP(vp)) { int ioflag; dp->i_ffs_nlink--; dp->i_flag |= IN_CHANGE; ip->i_ffs_nlink--; ip->i_flag |= IN_CHANGE; ioflag = DOINGASYNC(vp) ? 0 : IO_SYNC; error = UFS_TRUNCATE(ip, (off_t)0, ioflag, cnp->cn_cred); } cache_purge(vp); #ifdef UFS_DIRHASH /* Kill any active hash; i_effnlink == 0, so it will not come back. */ if (ip->i_dirhash != NULL) ufsdirhash_free(ip); #endif out: VN_KNOTE(vp, NOTE_DELETE); vput(dvp); vput(vp); return (error); } /* * symlink -- make a symbolic link */ int ufs_symlink(v) void *v; { struct vop_symlink_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; char *a_target; } */ *ap = v; struct vnode *vp, **vpp = ap->a_vpp; struct inode *ip; int len, error; error = ufs_makeinode(IFLNK | ap->a_vap->va_mode, ap->a_dvp, vpp, ap->a_cnp); if (error) return (error); VN_KNOTE(ap->a_dvp, NOTE_WRITE); vp = *vpp; len = strlen(ap->a_target); if (len < vp->v_mount->mnt_maxsymlinklen) { ip = VTOI(vp); bcopy(ap->a_target, (char *)ip->i_ffs_shortlink, len); ip->i_ffs_size = len; ip->i_flag |= IN_CHANGE | IN_UPDATE; } else error = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0, UIO_SYSSPACE, IO_NODELOCKED, ap->a_cnp->cn_cred, NULL, (struct proc *)0); vput(vp); return (error); } /* * Vnode op for reading directories. * * The routine below assumes that the on-disk format of a directory * is the same as that defined by . If the on-disk * format changes, then it will be necessary to do a conversion * from the on-disk format that read returns to the format defined * by . */ int ufs_readdir(v) void *v; { struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; int *a_eofflag; u_long **a_cookies; int *ncookies; } */ *ap = v; struct uio *uio = ap->a_uio; int error; size_t count, lost; off_t off = uio->uio_offset; count = uio->uio_resid; /* Make sure we don't return partial entries. */ count -= (uio->uio_offset + count) & (DIRBLKSIZ -1); if (count <= 0) return (EINVAL); lost = uio->uio_resid - count; uio->uio_resid = count; uio->uio_iov->iov_len = count; # if (BYTE_ORDER == LITTLE_ENDIAN) if (ap->a_vp->v_mount->mnt_maxsymlinklen > 0) { error = VOP_READ(ap->a_vp, uio, 0, ap->a_cred); } else { struct dirent *dp, *edp; struct uio auio; struct iovec aiov; caddr_t dirbuf; int readcnt; u_char tmp; auio = *uio; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_segflg = UIO_SYSSPACE; aiov.iov_len = count; MALLOC(dirbuf, caddr_t, count, M_TEMP, M_WAITOK); aiov.iov_base = dirbuf; error = VOP_READ(ap->a_vp, &auio, 0, ap->a_cred); if (error == 0) { readcnt = count - auio.uio_resid; edp = (struct dirent *)&dirbuf[readcnt]; for (dp = (struct dirent *)dirbuf; dp < edp; ) { tmp = dp->d_namlen; dp->d_namlen = dp->d_type; dp->d_type = tmp; if (dp->d_reclen > 0) { dp = (struct dirent *) ((char *)dp + dp->d_reclen); } else { error = EIO; break; } } if (dp >= edp) error = uiomove(dirbuf, readcnt, uio); } FREE(dirbuf, M_TEMP); } # else error = VOP_READ(ap->a_vp, uio, 0, ap->a_cred); # endif if (!error && ap->a_ncookies) { struct dirent *dp, *dpstart; off_t offstart; u_long *cookies; int ncookies; /* * Only the NFS server and emulations use cookies, and they * load the directory block into system space, so we can * just look at it directly. */ if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1) panic("ufs_readdir: lost in space"); dpstart = (struct dirent *) (uio->uio_iov->iov_base - (uio->uio_offset - off)); offstart = off; for (dp = dpstart, ncookies = 0; off < uio->uio_offset; ) { if (dp->d_reclen == 0) break; off += dp->d_reclen; ncookies++; dp = (struct dirent *)((caddr_t)dp + dp->d_reclen); } lost += uio->uio_offset - off; uio->uio_offset = off; MALLOC(cookies, u_long *, ncookies * sizeof(u_long), M_TEMP, M_WAITOK); *ap->a_ncookies = ncookies; *ap->a_cookies = cookies; for (off = offstart, dp = dpstart; off < uio->uio_offset; ) { off += dp->d_reclen; *cookies = off; cookies++; dp = (struct dirent *)((caddr_t)dp + dp->d_reclen); } } uio->uio_resid += lost; *ap->a_eofflag = VTOI(ap->a_vp)->i_ffs_size <= uio->uio_offset; return (error); } /* * Return target name of a symbolic link */ int ufs_readlink(v) void *v; { struct vop_readlink_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap = v; struct vnode *vp = ap->a_vp; struct inode *ip = VTOI(vp); int isize; isize = ip->i_ffs_size; if (isize < vp->v_mount->mnt_maxsymlinklen || (vp->v_mount->mnt_maxsymlinklen == 0 && ip->i_ffs_blocks == 0)) { uiomove((char *)ip->i_ffs_shortlink, isize, ap->a_uio); return (0); } return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred)); } /* * Lock an inode. If its already locked, set the WANT bit and sleep. */ int ufs_lock(v) void *v; { struct vop_lock_args /* { struct vnode *a_vp; int a_flags; struct proc *a_p; } */ *ap = v; struct vnode *vp = ap->a_vp; return (lockmgr(&VTOI(vp)->i_lock, ap->a_flags, &vp->v_interlock, ap->a_p)); } /* * Unlock an inode. If WANT bit is on, wakeup. */ int ufs_unlock(v) void *v; { struct vop_unlock_args /* { struct vnode *a_vp; int a_flags; struct proc *a_p; } */ *ap = v; struct vnode *vp = ap->a_vp; return (lockmgr(&VTOI(vp)->i_lock, ap->a_flags | LK_RELEASE, &vp->v_interlock, ap->a_p)); } /* * Check for a locked inode. */ int ufs_islocked(v) void *v; { struct vop_islocked_args /* { struct vnode *a_vp; } */ *ap = v; return (lockstatus(&VTOI(ap->a_vp)->i_lock)); } /* * Calculate the logical to physical mapping if not done already, * then call the device strategy routine. */ int ufs_strategy(v) void *v; { struct vop_strategy_args /* { struct buf *a_bp; } */ *ap = v; struct buf *bp = ap->a_bp; struct vnode *vp = bp->b_vp; struct inode *ip; int error; int s; ip = VTOI(vp); if (vp->v_type == VBLK || vp->v_type == VCHR) panic("ufs_strategy: spec"); if (bp->b_blkno == bp->b_lblkno) { error = VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL); if (error) { bp->b_error = error; bp->b_flags |= B_ERROR; s = splbio(); biodone(bp); splx(s); return (error); } if ((long)bp->b_blkno == -1) clrbuf(bp); } if ((long)bp->b_blkno == -1) { s = splbio(); biodone(bp); splx(s); return (0); } vp = ip->i_devvp; bp->b_dev = vp->v_rdev; VOCALL (vp->v_op, VOFFSET(vop_strategy), ap); return (0); } /* * Print out the contents of an inode. */ int ufs_print(v) void *v; { struct vop_print_args /* { struct vnode *a_vp; } */ *ap = v; struct vnode *vp = ap->a_vp; struct inode *ip = VTOI(vp); printf("tag VT_UFS, ino %d, on dev %d, %d", ip->i_number, major(ip->i_dev), minor(ip->i_dev)); printf(" flags 0x%x, effnlink %d, nlink %d\n", ip->i_flag, ip->i_effnlink, ip->i_ffs_nlink); printf("\tmode 0%o, owner %d, group %d, size %lld", ip->i_ffs_mode, ip->i_ffs_uid, ip->i_ffs_gid, ip->i_ffs_size); #ifdef FIFO if (vp->v_type == VFIFO) fifo_printinfo(vp); #endif /* FIFO */ lockmgr_printinfo(&ip->i_lock); printf("\n"); return (0); } /* * Read wrapper for special devices. */ int ufsspec_read(v) void *v; { struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap = v; /* * Set access flag. */ VTOI(ap->a_vp)->i_flag |= IN_ACCESS; return (VOCALL (spec_vnodeop_p, VOFFSET(vop_read), ap)); } /* * Write wrapper for special devices. */ int ufsspec_write(v) void *v; { struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap = v; /* * Set update and change flags. */ VTOI(ap->a_vp)->i_flag |= IN_CHANGE | IN_UPDATE; return (VOCALL (spec_vnodeop_p, VOFFSET(vop_write), ap)); } /* * Close wrapper for special devices. * * Update the times on the inode then do device close. */ int ufsspec_close(v) void *v; { struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap = v; struct vnode *vp = ap->a_vp; struct inode *ip = VTOI(vp); simple_lock(&vp->v_interlock); if (ap->a_vp->v_usecount > 1) ITIMES(ip, &time, &time); simple_unlock(&vp->v_interlock); return (VOCALL (spec_vnodeop_p, VOFFSET(vop_close), ap)); } #ifdef FIFO /* * Read wrapper for fifo's */ int ufsfifo_read(v) void *v; { struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap = v; extern int (**fifo_vnodeop_p)(void *); /* * Set access flag. */ VTOI(ap->a_vp)->i_flag |= IN_ACCESS; return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_read), ap)); } /* * Write wrapper for fifo's. */ int ufsfifo_write(v) void *v; { struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap = v; extern int (**fifo_vnodeop_p)(void *); /* * Set update and change flags. */ VTOI(ap->a_vp)->i_flag |= IN_CHANGE | IN_UPDATE; return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_write), ap)); } /* * Close wrapper for fifo's. * * Update the times on the inode then do device close. */ int ufsfifo_close(v) void *v; { struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap = v; extern int (**fifo_vnodeop_p)(void *); struct vnode *vp = ap->a_vp; struct inode *ip = VTOI(vp); simple_lock(&vp->v_interlock); if (ap->a_vp->v_usecount > 1) ITIMES(ip, &time, &time); simple_unlock(&vp->v_interlock); return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_close), ap)); } #endif /* FIFO */ /* * Return POSIX pathconf information applicable to ufs filesystems. */ int ufs_pathconf(v) void *v; { struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; register_t *a_retval; } */ *ap = v; switch (ap->a_name) { case _PC_LINK_MAX: *ap->a_retval = LINK_MAX; return (0); case _PC_NAME_MAX: *ap->a_retval = NAME_MAX; return (0); case _PC_PATH_MAX: *ap->a_retval = PATH_MAX; return (0); case _PC_PIPE_BUF: *ap->a_retval = PIPE_BUF; return (0); case _PC_CHOWN_RESTRICTED: *ap->a_retval = 1; return (0); case _PC_NO_TRUNC: *ap->a_retval = 1; return (0); default: return (EINVAL); } /* NOTREACHED */ } /* * Advisory record locking support */ int ufs_advlock(v) void *v; { struct vop_advlock_args /* { struct vnode *a_vp; caddr_t a_id; int a_op; struct flock *a_fl; int a_flags; } */ *ap = v; struct inode *ip = VTOI(ap->a_vp); return (lf_advlock(&ip->i_lockf, ip->i_ffs_size, ap->a_id, ap->a_op, ap->a_fl, ap->a_flags)); } /* * Initialize the vnode associated with a new inode, handle aliased * vnodes. */ int ufs_vinit(mntp, specops, fifoops, vpp) struct mount *mntp; int (**specops)(void *); int (**fifoops)(void *); struct vnode **vpp; { struct inode *ip; struct vnode *vp, *nvp; vp = *vpp; ip = VTOI(vp); switch(vp->v_type = IFTOVT(ip->i_ffs_mode)) { case VCHR: case VBLK: vp->v_op = specops; if ((nvp = checkalias(vp, ip->i_ffs_rdev, mntp)) != NULL) { /* * Discard unneeded vnode, but save its inode. * Note that the lock is carried over in the inode * to the replacement vnode. */ nvp->v_data = vp->v_data; vp->v_data = NULL; vp->v_op = spec_vnodeop_p; #ifdef VFSDEBUG vp->v_flag &= ~VLOCKSWORK; #endif vrele(vp); vgone(vp); /* * Reinitialize aliased inode. */ vp = nvp; ip->i_vnode = vp; } break; case VFIFO: #ifdef FIFO vp->v_op = fifoops; break; #else return (EOPNOTSUPP); #endif case VNON: case VBAD: case VSOCK: case VLNK: case VDIR: case VREG: break; } if (ip->i_number == ROOTINO) vp->v_flag |= VROOT; /* * Initialize modrev times */ SETHIGH(ip->i_modrev, mono_time.tv_sec); SETLOW(ip->i_modrev, mono_time.tv_usec * 4294); *vpp = vp; return (0); } /* * Allocate a new inode. */ int ufs_makeinode(mode, dvp, vpp, cnp) int mode; struct vnode *dvp; struct vnode **vpp; struct componentname *cnp; { struct inode *ip, *pdir; struct direct newdir; struct vnode *tvp; int error; pdir = VTOI(dvp); #ifdef DIAGNOSTIC if ((cnp->cn_flags & HASBUF) == 0) panic("ufs_makeinode: no name"); #endif *vpp = NULL; if ((mode & IFMT) == 0) mode |= IFREG; if ((error = UFS_INODE_ALLOC(pdir, mode, cnp->cn_cred, &tvp)) != 0) { pool_put(&namei_pool, cnp->cn_pnbuf); vput(dvp); return (error); } ip = VTOI(tvp); ip->i_ffs_gid = pdir->i_ffs_gid; ip->i_ffs_uid = cnp->cn_cred->cr_uid; if ((error = getinoquota(ip)) || (error = ufs_quota_alloc_inode(ip, cnp->cn_cred))) { pool_put(&namei_pool, cnp->cn_pnbuf); UFS_INODE_FREE(ip, ip->i_number, mode); vput(tvp); vput(dvp); return (error); } ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE; ip->i_ffs_mode = mode; tvp->v_type = IFTOVT(mode); /* Rest init'd in getnewvnode(). */ ip->i_effnlink = 1; ip->i_ffs_nlink = 1; if (DOINGSOFTDEP(tvp)) softdep_change_linkcnt(ip, 0); if ((ip->i_ffs_mode & ISGID) && !groupmember(ip->i_ffs_gid, cnp->cn_cred) && suser_ucred(cnp->cn_cred)) ip->i_ffs_mode &= ~ISGID; /* * Make sure inode goes to disk before directory entry. */ if ((error = UFS_UPDATE(ip, !DOINGSOFTDEP(tvp))) != 0) goto bad; ufs_makedirentry(ip, cnp, &newdir); if ((error = ufs_direnter(dvp, tvp, &newdir, cnp, NULL)) != 0) goto bad; if ((cnp->cn_flags & SAVESTART) == 0) pool_put(&namei_pool, cnp->cn_pnbuf); vput(dvp); *vpp = tvp; return (0); bad: /* * Write error occurred trying to update the inode * or the directory so must deallocate the inode. */ pool_put(&namei_pool, cnp->cn_pnbuf); vput(dvp); ip->i_effnlink = 0; ip->i_ffs_nlink = 0; ip->i_flag |= IN_CHANGE; if (DOINGSOFTDEP(tvp)) softdep_change_linkcnt(ip, 0); tvp->v_type = VNON; vput(tvp); return (error); } struct filterops ufsread_filtops = { 1, NULL, filt_ufsdetach, filt_ufsread }; struct filterops ufswrite_filtops = { 1, NULL, filt_ufsdetach, filt_ufswrite }; struct filterops ufsvnode_filtops = { 1, NULL, filt_ufsdetach, filt_ufsvnode }; int ufs_kqfilter(v) void *v; { struct vop_kqfilter_args /* { struct vnode *a_vp; struct knote *a_kn; } */ *ap = v; struct vnode *vp = ap->a_vp; struct knote *kn = ap->a_kn; switch (kn->kn_filter) { case EVFILT_READ: kn->kn_fop = &ufsread_filtops; break; case EVFILT_WRITE: kn->kn_fop = &ufswrite_filtops; break; case EVFILT_VNODE: kn->kn_fop = &ufsvnode_filtops; break; default: return (1); } kn->kn_hook = (caddr_t)vp; simple_lock(&vp->v_selectinfo.vsi_lock); SLIST_INSERT_HEAD(&vp->v_selectinfo.vsi_selinfo.si_note, kn, kn_selnext); simple_unlock(&vp->v_selectinfo.vsi_lock); return (0); } void filt_ufsdetach(struct knote *kn) { struct vnode *vp = (struct vnode *)kn->kn_hook; simple_lock(&vp->v_selectinfo.vsi_lock); SLIST_REMOVE(&vp->v_selectinfo.vsi_selinfo.si_note, kn, knote, kn_selnext); simple_unlock(&vp->v_selectinfo.vsi_lock); } /*ARGSUSED*/ int filt_ufsread(struct knote *kn, long hint) { struct vnode *vp = (struct vnode *)kn->kn_hook; struct inode *ip = VTOI(vp); /* * filesystem is gone, so set the EOF flag and schedule * the knote for deletion. */ if (hint == NOTE_REVOKE) { kn->kn_flags |= (EV_EOF | EV_ONESHOT); return (1); } kn->kn_data = ip->i_ffs_size - kn->kn_fp->f_offset; if (kn->kn_data == 0 && kn->kn_sfflags & NOTE_EOF) { kn->kn_fflags |= NOTE_EOF; return (1); } return (kn->kn_data != 0); } int filt_ufswrite(struct knote *kn, long hint) { /* * filesystem is gone, so set the EOF flag and schedule * the knote for deletion. */ if (hint == NOTE_REVOKE) { kn->kn_flags |= (EV_EOF | EV_ONESHOT); return (1); } kn->kn_data = 0; return (1); } int filt_ufsvnode(struct knote *kn, long hint) { if (kn->kn_sfflags & hint) kn->kn_fflags |= hint; if (hint == NOTE_REVOKE) { kn->kn_flags |= EV_EOF; return (1); } return (kn->kn_fflags != 0); } makefs/src/sys/ufs/ufs/ufsmount.h010064400000000000000000000071431314214547600143170ustar00/* $OpenBSD: ufsmount.h,v 1.11 2005/07/03 20:14:03 drahn Exp $ */ /* $NetBSD: ufsmount.h,v 1.4 1994/12/21 20:00:23 mycroft Exp $ */ /* * Copyright (c) 1982, 1986, 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. * * @(#)ufsmount.h 8.4 (Berkeley) 10/27/94 */ struct buf; struct inode; struct nameidata; struct timeval; struct ucred; struct uio; struct vnode; struct netexport; /* This structure describes the UFS specific mount structure data. */ struct ufsmount { struct mount *um_mountp; /* filesystem vfs structure */ dev_t um_dev; /* device mounted */ struct vnode *um_devvp; /* block device mounted vnode */ u_long um_fstype; /* type of filesystem */ union { /* pointer to superblock */ struct fs *fs; /* FFS */ struct lfs *lfs; /* LFS */ struct m_ext2fs *e2fs; /* EXT2FS */ } ufsmount_u; #define um_fs ufsmount_u.fs #define um_lfs ufsmount_u.lfs #define um_e2fs ufsmount_u.e2fs #define um_e2fsb ufsmount_u.e2fs->s_es struct vnode *um_quotas[MAXQUOTAS]; /* pointer to quota files */ struct ucred *um_cred[MAXQUOTAS]; /* quota file access cred */ u_long um_nindir; /* indirect ptrs per block */ u_long um_bptrtodb; /* indir ptr to disk block */ u_long um_seqinc; /* inc between seq blocks */ time_t um_btime[MAXQUOTAS]; /* block quota time limit */ time_t um_itime[MAXQUOTAS]; /* inode quota time limit */ char um_qflags[MAXQUOTAS]; /* quota specific flags */ struct netexport um_export; /* export information */ u_int64_t um_savedmaxfilesize; /* XXX - limit maxfilesize */ }; /* * Filesystem types */ #define UM_UFS1 1 #define UM_UFS2 2 #define UM_EXT2FS 3 #define UM_LFS 4 /* * Flags describing the state of quotas. */ #define QTF_OPENING 0x01 /* Q_QUOTAON in progress */ #define QTF_CLOSING 0x02 /* Q_QUOTAOFF in progress */ /* Convert mount ptr to ufsmount ptr. */ #define VFSTOUFS(mp) ((struct ufsmount *)((mp)->mnt_data)) /* * Macros to access filesystem parameters in the ufsmount structure. * Used by ufs_bmap. */ #define MNINDIR(ump) ((ump)->um_nindir) #define blkptrtodb(ump, b) ((b) << (ump)->um_bptrtodb) #define is_sequential(ump, a, b) ((b) == (a) + ump->um_seqinc) makefs/src/usr.sbin/Makefile.inc010064400000000000000000000004441341415245500140230ustar00# $MirOS: src/usr.sbin/Makefile.inc,v 1.5 2019/01/05 16:02:26 tg Exp $ # $OpenBSD: Makefile.inc,v 1.2 1997/09/21 11:43:04 deraadt Exp $ .ifndef _MODSRC_USR_SBIN_MAKEFILE_INC _MODSRC_USR_SBIN_MAKEFILE_INC=1 BINDIR?= /usr/sbin .ifndef GNUPORT LDFLAGS+= -Wl,-rpath -Wl,/usr/lib .endif .endif makefs/src/usr.sbin/makefs/Makefile010064400000000000000000000037561341415673100145330ustar00# $MirOS: src/usr.sbin/makefs/Makefile,v 1.28 2019/01/05 16:39:26 tg Exp $ # $NetBSD: Makefile,v 1.25 2009/04/22 15:23:05 lukem Exp $ WARNS?= 3 # XXX -Wsign-compare .include PROG= makefs SRCS= cd9660.c ffs.c \ getid.c \ makefs.c misc.c \ pack_dev.c \ spec.c \ walk.c MAN= makefs.8 OStype?= unknown .if (${OStype} == "MirBSD") || defined(GNUPORT) MAKEFS_PORT= #defined .endif .ifdef MAKEFS_PORT HOSTPROG= #defined, empty NETBSDSRCDIR?= ${.CURDIR}/nbsrc CDIAGFLAGS+= -Wmissing-declarations -Wmissing-prototypes CPPFLAGS+= -DLIBC_SCCS -DHAVE_STRUCT_STAT_BIRTHTIME=0 \ -DHAVE_NBTOOL_CONFIG_H=0 -DHAVE_NETDB_H=1 \ -DHAVE_PWCACHE_USERDB=0 -DHAVE_STRSUFTOLL=0 .endif MKNODSRC= ${NETBSDSRCDIR}/sbin/mknod MTREESRC= ${NETBSDSRCDIR}/usr.sbin/mtree CPPFLAGS+= -I${.CURDIR} -I${MKNODSRC} -I${MTREESRC} .PATH: ${MKNODSRC} ${MTREESRC} .include "cd9660/Makefile.inc" .include "ffs/Makefile.inc" .ifdef MAKEFS_PORT CPPFLAGS+= -I${NETBSDSRCDIR}/sys \ -I${BSDSRCDIR}/sbin/mknod -I${BSDSRCDIR}/usr.sbin/mtree \ -I${BSDSRCDIR}/sys -I${BSDSRCDIR}/sys/isofs/cd9660 .PATH: ${BSDSRCDIR}/sys/ufs/ffs ${BSDSRCDIR}/sys/isofs/cd9660 \ ${BSDSRCDIR}/sbin/mknod ${BSDSRCDIR}/usr.sbin/mtree .endif .if !defined(GNUPORT) && (${HOSTPROG:U} == "") DPADD+= ${LIBUTIL} LDADD+= -lutil .endif .ifdef MAKEFS_PORT SRCS+= pwcache.c CPPFLAGS+= -I${NETBSDSRCDIR}/lib/libc/gen .PATH: ${NETBSDSRCDIR}/lib/libc/gen SRCS+= strsuftoll.c CPPFLAGS+= -I${NETBSDSRCDIR}/lib/libc/stdlib .PATH: ${NETBSDSRCDIR}/lib/libc/stdlib . ifndef GNUPORT SRCS+= stat_flags.c .PATH: ${NETBSDSRCDIR}/lib/libutil . else CPPFLAGS+= -D_GNU_SOURCE -DGNUPORT SRCS+= fparseln.c .PATH: ${BSDSRCDIR}/lib/libutil CPPFLAGS+= -I${BSDSRCDIR}/include SRCS+= setmode.c unvis.c .PATH: ${BSDSRCDIR}/lib/libc/gen SRCS+= fgetln.c .PATH: ${BSDSRCDIR}/../contrib/code/mirmake/dist/contrib SRCS+= strlfun.c CPPFLAGS+= -DOUTSIDE_OF_LIBKERN -DNEED_STRLFUN_PROTOS CPPFLAGS+= -DL_strlcpy -DL_strlcat .PATH: ${BSDSRCDIR}/kern/c . endif .endif .include makefs/src/usr.sbin/makefs/README010064400000000000000000000111141314214553000137270ustar00$NetBSD: README,v 1.4 2009/01/03 08:25:35 lukem Exp $ makefs - build a filesystem image from a directory tree NOTES: * This tool uses modified local copies of source found in other parts of the tree. This is intentional. * makefs is a work in progress, and subject to change. user overview: -------------- makefs creates a filesystem image from a given directory tree. the following filesystem types can be built: ffs BSD fast filesystem cd9660 ISO 9660 filesystem Support for the following filesystems maybe be added in the future ext2fs Linux EXT2 filesystem fat MS-DOS `FAT' filesystem (FAT12, FAT16, FAT32) Various filesystem independent parameters and contraints can be specified, such as: - minimum filesystem size (in KB) - maximum filesystem size (in KB) - free inodes - free blocks (in KB) - mtree(8) specification file containing permissions and ownership to use in image, overridding the settings in the directory tree - file containing list of files to specifically exclude or include - fnmatch(3) pattern of filenames to exclude or include - endianness of target filesystem Filesystem specific parameters can be given as well, with a command line option such as "-o fsspeccific-options,comma-separated". For example, ffs would allow tuning of: - block & fragment size - cylinder groups - number of blocks per inode - minimum free space Other filesystems might have controls on how to "munge" file names to fit within the constraints of the target filesystem. Exit codes: 0 all ok 1 fatal error 2 some files couldn't be added during image creation (bad perms, missing file, etc). image will continue to be made Implementation overview: ------------------------ The implementation must allow for easy addition of extra filesystems with minimal changes to the filesystem independent sections. The main program will: - parse the options, including calling fs-specific routines to validate fs-specific options - walk the tree, building up a data structure which represents the tree to stuff into the image. The structure will probably be a similar tree to what mtree(8) uses internally; a linked list of entries per directory with a child pointer to children of directories. ".." won't be stored in the list; the fs-specific tree walker should add this if required by the fs. this builder have the smarts to handle hard links correctly. - (optionally) Change the permissions in the tree according to the mtree(8) specfile - Call an fs-specific routine to build the image based on the data structures. Each fs-specific module should have the following external interfaces: prepare_options optional filesystem specific defaults that need to be setup before parsing fs-specific options. parse_options parse the string for fs-specific options, feeding errors back to the user as appropriate cleanup_options optional filesystem specific data that need to be cleaned up when done with this filesystem. make_fs take the data structures representing the directory tree and fs parameters, validate that the parameters are valid (e.g, the requested image will be large enough), create the image, and populate the image prepare_options and cleanup_options are optional and can be NULL. NOTE: All filesystem specific options are referenced via the fs_specific pointer from the fsinfo_t strucutre. It is up to the filesystem to allocate and free any data needed for this via the prepare and cleanup callbacks. Each fs-specific module will need to add it's routines to the dispatch array in makefs.c and add prototypes for these to makefs.h All other implementation details should not need to change any of the generic code. ffs implementation ------------------ In the ffs case, we can leverage off sbin/newfs/mkfs.c to actually build the image. When building and populating the image, the implementation can be greatly simplified if some assumptions are made: - the total required size (in blocks and inodes) is determined as part of the validation phase - a "file" (including a directory) has a known size, so support for growing a file is not necessary Two underlying primitives are provided: make_inode create an inode, returning the inode number write_file write file (from memory if DIR, file descriptor if FILE or SYMLINK), referencing given inode. it is smart enough to know if a short symlink can be stuffed into the inode, etc. When creating a directory, the directory entries in the previously built tree data structure is scanned and built in memory so it can be written entirely as a single write_file() operation. makefs/src/usr.sbin/makefs/TODO010064400000000000000000000017321314214553000135440ustar00$NetBSD: TODO,v 1.7 2007/12/10 23:54:35 dyoung Exp $ todo ---- - read files from multiple directories with or without root specification, e.g., makefs -t cd9660 output.iso dir1 root2=dir2 dir3 root4=dir4 - display block numbers for a given file (e.g, /boot) - finish makefs.8 - testing - even more testing - add support for converting a tar file (instead of a directory tree); suggested by kpneal@pobox.com outstanding bugs ---------------- - size estimation is still out (need to take into account indirect blocks!) - parameter checking when density is rather high or low. - filling up a filesystem (running out of inodes or whatever) doesn't do the right thing. discuss ------- - consider replacing ffs_balloc() et al with own code that doesn't need hacked-up buf.c code - whacking on newfs/mkfs.c to allow .PATH-ing directly into makefs(8). this would involve passing all of mkfs()'s parameters in a single struct rather than a lot of global vars, etc. makefs/src/usr.sbin/makefs/cd9660.c010064400000000000000000001757441134456302700141610ustar00/* $NetBSD: cd9660.c,v 1.26 2009/01/16 18:02:24 pooka Exp $ */ /* * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan * Perez-Rathke and Ram Vedam. All rights reserved. * * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, * Alan Perez-Rathke and Ram Vedam. * * 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 DANIEL WATT, WALTER DEIGNAN, RYAN * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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. */ /* * Copyright (c) 2001 Wasabi Systems, Inc. * All rights reserved. * * Written by Luke Mewburn 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. */ /* * Copyright (c) 1982, 1986, 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. * */ #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #else #ifdef __MirBSD__ #include #endif #include #endif #include #if defined(__RCSID) && !defined(__lint) __IDSTRING(mbsdid, "$MirOS: src/usr.sbin/makefs/cd9660.c,v 1.27 2010/03/06 23:32:15 tg Exp $"); __RCSID("$NetBSD: cd9660.c,v 1.26 2009/01/16 18:02:24 pooka Exp $"); #endif /* !__lint */ #include #include #include #include #include #include "makefs.h" #include "cd9660.h" #include "cd9660/iso9660_rrip.h" #include "cd9660/cd9660_archimedes.h" /* * Global variables */ iso9660_disk diskStructure; static void cd9660_finalize_PVD(void); static cd9660node *cd9660_allocate_cd9660node(void); static void cd9660_set_defaults(void); static int cd9660_arguments_set_string(const char *, const char *, int, char, char *); static void cd9660_populate_iso_dir_record( struct _iso_directory_record_cd9660 *, u_char, u_char, u_char, const char *); static void cd9660_setup_root_node(fsnode *); static int cd9660_setup_volume_descriptors(void); #if 0 static int cd9660_fill_extended_attribute_record(cd9660node *); #endif static void cd9660_sort_nodes(cd9660node *); static int cd9960_translate_node_common(cd9660node *); static int cd9660_translate_node(fsnode *, cd9660node *); static int cd9660_compare_filename(const char *, const char *); static void cd9660_sorted_child_insert(cd9660node *, cd9660node *); static int cd9660_handle_collisions(cd9660node *, int); static cd9660node *cd9660_rename_filename(cd9660node *, int, int); static void cd9660_copy_filenames(cd9660node *); static void cd9660_sorting_nodes(cd9660node *); static int cd9660_count_collisions(cd9660node *); static cd9660node *cd9660_rrip_move_directory(cd9660node *); static int cd9660_add_dot_records(cd9660node *); static void cd9660_convert_structure(fsnode *, cd9660node *, int, int *, int *); static void cd9660_free_structure(cd9660node *); static int cd9660_generate_path_table(void); static int cd9660_level1_convert_filename(const char *, char *, size_t, int) __bounded(string, 2, 3); static int cd9660_level2_convert_filename(const char *, char *, size_t, int) __bounded(string, 2, 3); #if 0 static int cd9660_joliet_convert_filename(const char *, char *, size_t, int) __bounded(string, 2, 3); #endif static int cd9660_convert_filename(const char *, char *, size_t, int) __bounded(string, 2, 3); static void cd9660_populate_dot_records(cd9660node *); static int cd9660_compute_offsets(cd9660node *, int); #if 0 static int cd9660_copy_stat_info(cd9660node *, cd9660node *, int); #endif static cd9660node *cd9660_create_virtual_entry(const char *, cd9660node *, int, int); static cd9660node *cd9660_create_file(const char *, cd9660node *, cd9660node *); static cd9660node *cd9660_create_directory(const char *, cd9660node *, cd9660node *); static cd9660node *cd9660_create_special_directory(u_char, cd9660node *); /* * Allocate and initalize a cd9660node * @returns struct cd9660node * Pointer to new node, or NULL on error */ static cd9660node * cd9660_allocate_cd9660node(void) { cd9660node *temp; if ((temp = calloc(1, sizeof(cd9660node))) == NULL) err(EXIT_FAILURE, "%s: calloc", __func__); TAILQ_INIT(&temp->cn_children); temp->parent = temp->dot_record = temp->dot_dot_record = NULL; temp->ptnext = temp->ptprev = temp->ptlast = NULL; temp->node = NULL; temp->isoDirRecord = NULL; temp->isoExtAttributes = NULL; temp->rr_real_parent = temp->rr_relocated = NULL; temp->su_tail_data = NULL; return temp; } int cd9660_defaults_set = 0; int rrip_squash = 0; /** * Set default values for cd9660 extension to makefs */ static void cd9660_set_defaults(void) { /*Fix the sector size for now, though the spec allows for other sizes*/ diskStructure.sectorSize = 2048; /* Set up defaults in our own structure */ diskStructure.verbose_level = 0; diskStructure.keep_bad_images = 0; diskStructure.follow_sym_links = 0; diskStructure.isoLevel = 2; diskStructure.rock_ridge_enabled = 0; diskStructure.rock_ridge_renamed_dir_name = 0; diskStructure.rock_ridge_move_count = 0; diskStructure.rr_moved_dir = 0; diskStructure.archimedes_enabled = 0; diskStructure.include_padding_areas = 1; /* Spec breaking functionality */ diskStructure.allow_deep_trees = diskStructure.allow_start_dot = diskStructure.allow_max_name = diskStructure.allow_illegal_chars = diskStructure.allow_lowercase = diskStructure.allow_multidot = diskStructure.hide_rr_moved = diskStructure.omit_trailing_period = 0; /* Make sure the PVD is clear */ memset(&diskStructure.primaryDescriptor, 0, 2048); memset(diskStructure.primaryDescriptor.volume_set_id, 0x20, sizeof (diskStructure.primaryDescriptor.volume_set_id)); memset(diskStructure.primaryDescriptor.publisher_id, 0x20, sizeof (diskStructure.primaryDescriptor.publisher_id)); memset(diskStructure.primaryDescriptor.preparer_id, 0x20, sizeof (diskStructure.primaryDescriptor.preparer_id)); memset(diskStructure.primaryDescriptor.application_id, 0x20, sizeof (diskStructure.primaryDescriptor.application_id)); memset(diskStructure.primaryDescriptor.copyright_file_id, 0x20, sizeof (diskStructure.primaryDescriptor.copyright_file_id)); memset(diskStructure.primaryDescriptor.abstract_file_id, 0x20, sizeof (diskStructure.primaryDescriptor.abstract_file_id)); memset(diskStructure.primaryDescriptor.bibliographic_file_id, 0x20, sizeof (diskStructure.primaryDescriptor.bibliographic_file_id)); strlcpy(diskStructure.primaryDescriptor.system_id, ISO_DEFAULT_SYSID, sizeof (diskStructure.primaryDescriptor.system_id)); diskStructure.forced_creation_date = NULL; diskStructure.forced_modification_date = NULL; diskStructure.forced_expiration_date = NULL; diskStructure.forced_effective_date = NULL; cd9660_defaults_set = 1; /* Boot support: Initially disabled */ diskStructure.has_generic_bootimage = 0; diskStructure.generic_bootimage = NULL; diskStructure.boot_image_directory = 0; /*memset(diskStructure.boot_descriptor, 0, 2048);*/ diskStructure.is_bootable = 0; TAILQ_INIT(&diskStructure.boot_images); LIST_INIT(&diskStructure.boot_entries); } void cd9660_prep_opts(fsinfo_t *fsopts __unused) { cd9660_set_defaults(); } void cd9660_cleanup_opts(fsinfo_t *fsopts __unused) { } static int cd9660_arguments_set_string(const char *val, const char *fieldtitle, int length, char testmode, char * dest) { int len, test; if (val == NULL) warnx("error: The %s requires a string argument", fieldtitle); else if ((len = strlen(val)) <= length) { if (testmode == 'd') test = cd9660_valid_d_chars(val); else test = cd9660_valid_a_chars(val); if (test) { memcpy(dest, val, len); if (test == 2) cd9660_uppercase_characters(dest, len); return 1; } else warnx("error: The %s must be composed of " "%c-characters", fieldtitle, testmode); } else warnx("error: The %s must be at most 32 characters long", fieldtitle); return 0; } /* * Command-line parsing function */ int cd9660_parse_opts(const char *option, fsinfo_t *fsopts __unused) { char *var, *val; int rv; /* Set up allowed options - integer options ONLY */ option_t cd9660_options[] = { { "l", &diskStructure.isoLevel, 1, 3, "ISO Level" }, { "isolevel", &diskStructure.isoLevel, 1, 3, "ISO Level" }, { "verbose", &diskStructure.verbose_level, 0, 2, "Turns on verbose output" }, { "v", &diskStructure.verbose_level, 0 , 2, "Turns on verbose output"}, { .name = NULL } }; if (cd9660_defaults_set == 0) cd9660_set_defaults(); /* * Todo : finish implementing this, and make a function that * parses them */ /* string_option_t cd9660_string_options[] = { { "L", "Label", &diskStructure.primaryDescriptor.volume_id, 1, 32, "Disk Label", ISO_STRING_FILTER_DCHARS }, { NULL } } */ assert(option != NULL); if (debug & DEBUG_FS_PARSE_OPTS) printf("cd9660_parse_opts: got `%s'\n", option); if ((var = strdup(option)) == NULL) err(1, "allocating memory for copy of option string"); rv = 1; val = strchr(var, '='); if (val != NULL) *val++ = '\0'; /* First handle options with no parameters */ if (strcmp(var, "h") == 0) { diskStructure.displayHelp = 1; rv = 1; } else if (CD9660_IS_COMMAND_ARG_DUAL(var, "S", "follow-symlinks")) { /* this is not handled yet */ diskStructure.follow_sym_links = 1; rv = 1; } else if (CD9660_IS_COMMAND_ARG_DUAL(var, "L", "label")) { rv = cd9660_arguments_set_string(val, "Disk Label", sizeof (diskStructure.primaryDescriptor.volume_id), 'd', diskStructure.primaryDescriptor.volume_id); } else if (CD9660_IS_COMMAND_ARG_DUAL(var, "A", "applicationid")) { rv = cd9660_arguments_set_string(val, "Application Identifier", sizeof (diskStructure.primaryDescriptor.application_id), 'a', diskStructure.primaryDescriptor.application_id); } else if(CD9660_IS_COMMAND_ARG_DUAL(var, "P", "publisher")) { rv = cd9660_arguments_set_string(val, "Publisher Identifier", sizeof (diskStructure.primaryDescriptor.publisher_id), 'a', diskStructure.primaryDescriptor.publisher_id); } else if (CD9660_IS_COMMAND_ARG_DUAL(var, "p", "preparer")) { rv = cd9660_arguments_set_string(val, "Preparer Identifier", sizeof (diskStructure.primaryDescriptor.preparer_id), 'a', diskStructure.primaryDescriptor.preparer_id); } else if (CD9660_IS_COMMAND_ARG_DUAL(var, "V", "volumeid")) { rv = cd9660_arguments_set_string(val, "Volume Set Identifier", sizeof (diskStructure.primaryDescriptor.volume_set_id), 'a', diskStructure.primaryDescriptor.volume_set_id); /* Boot options */ } else if (CD9660_IS_COMMAND_ARG_DUAL(var, "B", "bootimage")) { if (val == NULL) warnx("error: The Boot Image parameter requires a valid boot information string"); else rv = cd9660_add_boot_disk(val); } else if (CD9660_IS_COMMAND_ARG(var, "bootimagedir")) { /* * XXXfvdl this is unused. */ if (val == NULL) errx(1, "error: The Boot Image Directory parameter" " requires a directory name\n"); else { if ((diskStructure.boot_image_directory = calloc(1, strlen(val) + 1)) == NULL) { CD9660_MEM_ALLOC_ERROR("cd9660_parse_opts"); exit(1); } /* BIG TODO: Add the max length function here */ cd9660_arguments_set_string(val, "Boot Image Directory", sizeof (diskStructure.boot_image_directory), 'd', diskStructure.boot_image_directory); } } else if (CD9660_IS_COMMAND_ARG_DUAL(var, "G", "generic-bootimage")) { if (val == NULL) warnx("error: The Boot Image parameter requires a valid boot information string"); else rv = cd9660_add_generic_bootimage(val); } else if (CD9660_IS_COMMAND_ARG(var, "no-trailing-padding")) diskStructure.include_padding_areas = 0; /* RRIP */ else if (CD9660_IS_COMMAND_ARG_DUAL(var, "R", "rockridge")) diskStructure.rock_ridge_enabled = 1; else if (CD9660_IS_COMMAND_ARG_DUAL(var, "A", "archimedes")) diskStructure.archimedes_enabled = 1; else if (CD9660_IS_COMMAND_ARG_DUAL(var, "K", "keep-bad-images")) diskStructure.keep_bad_images = 1; else if (CD9660_IS_COMMAND_ARG(var, "rr-squash")) rrip_squash = 1; else if (CD9660_IS_COMMAND_ARG(var, "hide-rr-moved")) diskStructure.hide_rr_moved = 1; else if (CD9660_IS_COMMAND_ARG(var, "allow-deep-trees")) diskStructure.allow_deep_trees = 1; else if (CD9660_IS_COMMAND_ARG(var, "allow-max-name")) diskStructure.allow_max_name = 1; else if (CD9660_IS_COMMAND_ARG(var, "allow-illegal-chars")) diskStructure.allow_illegal_chars = 1; else if (CD9660_IS_COMMAND_ARG(var, "allow-lowercase")) diskStructure.allow_lowercase = 1; else if (CD9660_IS_COMMAND_ARG(var,"allow-multidot")) diskStructure.allow_multidot = 1; else if (CD9660_IS_COMMAND_ARG(var, "omit-trailing-period")) diskStructure.omit_trailing_period = 1; else if (CD9660_IS_COMMAND_ARG(var, "no-emul-boot") || CD9660_IS_COMMAND_ARG(var, "no-boot") || CD9660_IS_COMMAND_ARG(var, "boot-info-table") || CD9660_IS_COMMAND_ARG(var, "hard-disk-boot")) { cd9660_eltorito_add_boot_option(var, 0); /* End of flag variables */ } else if (CD9660_IS_COMMAND_ARG(var, "boot-load-segment")) { if (val == NULL) { warnx("Option `%s' doesn't contain a value", var); rv = 0; } else { cd9660_eltorito_add_boot_option(var, val); } } else if (CD9660_IS_COMMAND_ARG(var, "creation-date")) { if ((rv = cd9660_isthisa_time_8426_utc(val, "creation-date"))) diskStructure.forced_creation_date = strdup(val); } else if (CD9660_IS_COMMAND_ARG(var, "modification-date")) { if ((rv = cd9660_isthisa_time_8426_utc(val, "modification-date"))) diskStructure.forced_modification_date = strdup(val); } else if (CD9660_IS_COMMAND_ARG(var, "expiration-date")) { if ((rv = cd9660_isthisa_time_8426_utc(val, "expiration-date"))) diskStructure.forced_expiration_date = strdup(val); } else if (CD9660_IS_COMMAND_ARG(var, "effective-date")) { if ((rv = cd9660_isthisa_time_8426_utc(val, "effective-date"))) diskStructure.forced_effective_date = strdup(val); } else { if (val == NULL) { warnx("Option `%s' doesn't contain a value", var); rv = 0; } else rv = set_option(cd9660_options, var, val); } if (var) free(var); return (rv); } /* * Main function for cd9660_makefs * Builds the ISO image file * @param const char *image The image filename to create * @param const char *dir The directory that is being read * @param struct fsnode *root The root node of the filesystem tree * @param struct fsinfo_t *fsopts Any options */ void cd9660_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts __unused) { int startoffset; int numDirectories; int pathTableSectors; int firstAvailableSector; int totalSpace; int error; cd9660node *real_root; if (diskStructure.verbose_level > 0) printf("cd9660_makefs: ISO level is %i\n", diskStructure.isoLevel); if (diskStructure.isoLevel < 2 && diskStructure.allow_multidot) errx(1, "allow-multidot requires iso level of 2\n"); assert(image != NULL); assert(dir != NULL); assert(root != NULL); if (diskStructure.displayHelp) { /* * Display help here - probably want to put it in * a separate function */ return; } diskStructure.rootFilesystemPath = dir; if (diskStructure.verbose_level > 0) printf("cd9660_makefs: image %s directory %s root %p\n", image, dir, root); /* Set up some constants. Later, these will be defined with options */ /* Counter needed for path tables */ numDirectories = 0; /* Convert tree to our own format */ /* Actually, we now need to add the REAL root node, at level 0 */ real_root = cd9660_allocate_cd9660node(); if ((real_root->isoDirRecord = calloc(1, sizeof (iso_directory_record_cd9660))) == NULL) { CD9660_MEM_ALLOC_ERROR("cd9660_makefs"); exit(1); } real_root->level = 0; diskStructure.rootNode = real_root; real_root->type = CD9660_TYPE_DIR; error = 0; real_root->node = root; cd9660_convert_structure(root, real_root, 1, &numDirectories, &error); if (TAILQ_EMPTY(&real_root->cn_children)) { errx(1, "cd9660_makefs: converted directory is empty. " "Tree conversion failed\n"); } else if (error != 0) { errx(1, "cd9660_makefs: tree conversion failed\n"); } else { if (diskStructure.verbose_level > 0) printf("cd9660_makefs: tree converted\n"); } /* Add the dot and dot dot records */ cd9660_add_dot_records(real_root); cd9660_setup_root_node(root); if (diskStructure.verbose_level > 0) printf("cd9660_makefs: done converting tree\n"); /* non-SUSP extensions */ if (diskStructure.archimedes_enabled) archimedes_convert_tree(diskStructure.rootNode); /* Rock ridge / SUSP init pass */ if (diskStructure.rock_ridge_enabled) { cd9660_susp_initialize(diskStructure.rootNode, diskStructure.rootNode, NULL, 0); } /* Build path table structure */ diskStructure.pathTableLength = cd9660_generate_path_table(); pathTableSectors = CD9660_BLOCKS(diskStructure.sectorSize, diskStructure.pathTableLength); firstAvailableSector = cd9660_setup_volume_descriptors(); if (diskStructure.is_bootable) { firstAvailableSector = cd9660_setup_boot(firstAvailableSector); if (firstAvailableSector < 0) errx(1, "setup_boot failed"); } /* LE first, then BE */ diskStructure.primaryLittleEndianTableSector = firstAvailableSector; diskStructure.primaryBigEndianTableSector = diskStructure.primaryLittleEndianTableSector + pathTableSectors; /* Set the secondary ones to -1, not going to use them for now */ diskStructure.secondaryBigEndianTableSector = -1; diskStructure.secondaryLittleEndianTableSector = -1; diskStructure.dataFirstSector = diskStructure.primaryBigEndianTableSector + pathTableSectors; if (diskStructure.verbose_level > 0) printf("cd9660_makefs: Path table conversion complete. " "Each table is %i bytes, or %i sectors.\n", diskStructure.pathTableLength, pathTableSectors); startoffset = diskStructure.sectorSize*diskStructure.dataFirstSector; totalSpace = cd9660_compute_offsets(real_root, startoffset); diskStructure.totalSectors = diskStructure.dataFirstSector + CD9660_BLOCKS(diskStructure.sectorSize, totalSpace); /* Disabled until pass 1 is done */ if (diskStructure.rock_ridge_enabled) { diskStructure.susp_continuation_area_start_sector = diskStructure.totalSectors; diskStructure.totalSectors += CD9660_BLOCKS(diskStructure.sectorSize, diskStructure.susp_continuation_area_size); cd9660_susp_finalize(diskStructure.rootNode); } cd9660_finalize_PVD(); /* Add padding sectors, just for testing purposes right now */ /* diskStructure.totalSectors+=150; */ /* Debugging output */ if (diskStructure.verbose_level > 0) { printf("cd9660_makefs: Sectors 0-15 reserved\n"); printf("cd9660_makefs: Primary path tables starts in sector %i\n", diskStructure.primaryLittleEndianTableSector); printf("cd9660_makefs: File data starts in sector %i\n", diskStructure.dataFirstSector); printf("cd9660_makefs: Total sectors: %i\n",diskStructure.totalSectors); } /* * Add padding sectors at the end * TODO: Clean this up and separate padding */ if (diskStructure.include_padding_areas) diskStructure.totalSectors += 150; cd9660_write_image(image); if (diskStructure.verbose_level > 1) { debug_print_volume_descriptor_information(); debug_print_tree(real_root,0); debug_print_path_tree(real_root); } /* Clean up data structures */ cd9660_free_structure(real_root); if (diskStructure.verbose_level > 0) printf("cd9660_makefs: done\n"); } /* Generic function pointer - implement later */ typedef int (*cd9660node_func)(cd9660node *); static void cd9660_finalize_PVD(void) { time_t tim; unsigned char *temp; /* Copy the root directory record */ temp = (unsigned char *) &diskStructure.primaryDescriptor; /* root should be a fixed size of 34 bytes since it has no name */ memcpy(diskStructure.primaryDescriptor.root_directory_record, diskStructure.rootNode->dot_record->isoDirRecord, 34); /* In RRIP, this might be longer than 34 */ diskStructure.primaryDescriptor.root_directory_record[0] = 34; /* Set up all the important numbers in the PVD */ cd9660_bothendian_dword(diskStructure.totalSectors, diskStructure.primaryDescriptor.volume_space_size); cd9660_bothendian_word(1, diskStructure.primaryDescriptor.volume_set_size); cd9660_bothendian_word(1, diskStructure.primaryDescriptor.volume_sequence_number); cd9660_bothendian_word(diskStructure.sectorSize, diskStructure.primaryDescriptor.logical_block_size); cd9660_bothendian_dword(diskStructure.pathTableLength, diskStructure.primaryDescriptor.path_table_size); cd9660_731(diskStructure.primaryLittleEndianTableSector, diskStructure.primaryDescriptor.type_l_path_table); cd9660_732(diskStructure.primaryBigEndianTableSector, diskStructure.primaryDescriptor.type_m_path_table); diskStructure.primaryDescriptor.file_structure_version[0] = 1; /* Pad all strings with spaces instead of nulls */ cd9660_pad_string_spaces(diskStructure.primaryDescriptor.volume_id); cd9660_pad_string_spaces(diskStructure.primaryDescriptor.system_id); cd9660_pad_string_spaces(diskStructure.primaryDescriptor.volume_set_id); cd9660_pad_string_spaces(diskStructure.primaryDescriptor.publisher_id); cd9660_pad_string_spaces(diskStructure.primaryDescriptor.preparer_id); cd9660_pad_string_spaces(diskStructure.primaryDescriptor.application_id); cd9660_pad_string_spaces(diskStructure.primaryDescriptor.copyright_file_id); cd9660_pad_string_spaces(diskStructure.primaryDescriptor.abstract_file_id); cd9660_pad_string_spaces(diskStructure.primaryDescriptor.bibliographic_file_id); /* Setup dates */ time(&tim); cd9660_time_8426(tim, diskStructure.primaryDescriptor.creation_date); cd9660_time_8426(tim, diskStructure.primaryDescriptor.modification_date); /* cd9660_set_date(now, diskStructure.primaryDescriptor.expiration_date); */ memcpy(diskStructure.primaryDescriptor.expiration_date, /* 16 "0" + one NUL */ "0000000000000000", 17); cd9660_time_8426(tim, diskStructure.primaryDescriptor.effective_date); if (diskStructure.forced_creation_date) memcpy(diskStructure.primaryDescriptor.creation_date, diskStructure.forced_creation_date, 17); if (diskStructure.forced_modification_date) memcpy(diskStructure.primaryDescriptor.modification_date, diskStructure.forced_modification_date, 17); if (diskStructure.forced_expiration_date) memcpy(diskStructure.primaryDescriptor.expiration_date, diskStructure.forced_expiration_date, 17); if (diskStructure.forced_effective_date) memcpy(diskStructure.primaryDescriptor.effective_date, diskStructure.forced_effective_date, 17); memcpy(diskStructure.primaryDescriptor.application_data, "[APPLICATION USE]--> ...", 24); memcpy(diskStructure.primaryDescriptor.application_data + sizeof (diskStructure.primaryDescriptor.application_data) - 24, "... <--[APPLICATION USE]", 24); } static void cd9660_name_iso_dir_record(struct _iso_directory_record_cd9660 *record, const char *name, u_char name_len) { record->name_len[0] = name_len; memset(record->name, '\0', sizeof (record->name)); memcpy(record->name, name, name_len); record->length[0] = ((33 + name_len + 1) >> 1) << 1; } static void cd9660_populate_iso_dir_record(struct _iso_directory_record_cd9660 *record, u_char ext_attr_length, u_char flags, u_char name_len, const char * name) { record->ext_attr_length[0] = ext_attr_length; record->flags[0] = ISO_FLAG_CLEAR | flags; record->file_unit_size[0] = 0; record->interleave[0] = 0; cd9660_bothendian_word(1, record->volume_sequence_number); cd9660_name_iso_dir_record(record, name, name_len); } static void cd9660_setup_root_node(fsnode *makefs_root) { cd9660_populate_iso_dir_record(diskStructure.rootNode->isoDirRecord, 0, ISO_FLAG_DIRECTORY, 1, "\0"); cd9660_time_915(makefs_root->inode->st.st_mtime, diskStructure.rootNode->isoDirRecord->date); cd9660_time_915(makefs_root->inode->st.st_mtime, diskStructure.rootNode->dot_record->isoDirRecord->date); cd9660_time_915(makefs_root->inode->st.st_mtime, diskStructure.rootNode->dot_dot_record->isoDirRecord->date); } /*********** SUPPORT FUNCTIONS ***********/ static int cd9660_setup_volume_descriptors(void) { /* Boot volume descriptor should come second */ int sector = 16; /* For now, a fixed 2 : PVD and terminator */ volume_descriptor *temp, *t; /* Set up the PVD */ if ((temp = calloc(1, sizeof (volume_descriptor))) == NULL) { CD9660_MEM_ALLOC_ERROR("cd9660_setup_volume_descriptors"); exit(1); } temp->volumeDescriptorData = (unsigned char *)&diskStructure.primaryDescriptor; temp->volumeDescriptorData[0] = ISO_VOLUME_DESCRIPTOR_PVD; temp->volumeDescriptorData[6] = 1; temp->sector = sector; memcpy(temp->volumeDescriptorData + 1, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5); diskStructure.firstVolumeDescriptor = temp; sector++; /* Set up boot support if enabled. BVD must reside in sector 17 */ if (diskStructure.is_bootable) { if ((t = calloc(1, sizeof (volume_descriptor))) == NULL) { CD9660_MEM_ALLOC_ERROR( "cd9660_setup_volume_descriptors"); exit(1); } if ((t->volumeDescriptorData = calloc(1, 2048)) == NULL) { CD9660_MEM_ALLOC_ERROR( "cd9660_setup_volume_descriptors"); exit(1); } temp->next = t; temp = t; t->sector = 17; if (diskStructure.verbose_level > 0) printf("Setting up boot volume descriptor\n"); cd9660_setup_boot_volume_descriptor(t); sector++; } /* Set up the terminator */ if ((t = calloc(1, sizeof (volume_descriptor))) == NULL) { CD9660_MEM_ALLOC_ERROR("cd9660_setup_volume_descriptors"); exit(1); } if ((t->volumeDescriptorData = calloc(1, 2048)) == NULL) { CD9660_MEM_ALLOC_ERROR("cd9660_setup_volume_descriptors"); exit(1); } temp->next = t; t->volumeDescriptorData[0] = ISO_VOLUME_DESCRIPTOR_TERMINATOR; t->next = 0; t->volumeDescriptorData[6] = 1; t->sector = sector; memcpy(t->volumeDescriptorData + 1, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5); sector++; return sector; } #if 0 /* * Populate EAR at some point. Not required, but is used by NetBSD's * cd9660 support */ static int cd9660_fill_extended_attribute_record(cd9660node *node) { if ((node->isoExtAttributes = calloc(1, sizeof (struct iso_extended_attributes))) == NULL) { CD9660_MEM_ALLOC_ERROR("cd9660_fill_extended_attribute_record"); exit(1); }; return 1; } #endif static int cd9960_translate_node_common(cd9660node *newnode) { time_t tim; int test; u_char flag; char temp[ISO_FILENAME_MAXLENGTH_WITH_PADDING]; /* Now populate the isoDirRecord structure */ memset(temp, 0, ISO_FILENAME_MAXLENGTH_WITH_PADDING); test = cd9660_convert_filename(newnode->node->name, temp, sizeof (temp), !(S_ISDIR(newnode->node->type))); flag = ISO_FLAG_CLEAR; if (S_ISDIR(newnode->node->type)) flag |= ISO_FLAG_DIRECTORY; cd9660_populate_iso_dir_record(newnode->isoDirRecord, 0, flag, strlen(temp), temp); /* Set the various dates */ /* If we want to use the current date and time */ time(&tim); cd9660_time_915(tim, newnode->isoDirRecord->date); cd9660_bothendian_dword(newnode->fileDataLength, newnode->isoDirRecord->size); /* If the file is a link, we want to set the size to 0 */ if (S_ISLNK(newnode->node->type)) newnode->fileDataLength = 0; return 1; } /* * Translate fsnode to cd9960node * Translate filenames and other metadata, including dates, sizes, * permissions, etc * @param struct fsnode * The node generated by makefs * @param struct cd9660node * The intermediate node to be written to * @returns int 0 on failure, 1 on success */ static int cd9660_translate_node(fsnode *node, cd9660node *newnode) { if (node == NULL) { if (diskStructure.verbose_level > 0) printf("cd9660_translate_node: NULL node passed, " "returning\n"); return 0; } if ((newnode->isoDirRecord = calloc(1, sizeof (iso_directory_record_cd9660))) == NULL) { CD9660_MEM_ALLOC_ERROR("cd9660_translate_node"); return 0; } /* Set the node pointer */ newnode->node = node; /* Set the size */ if (!(S_ISDIR(node->type))) newnode->fileDataLength = node->inode->st.st_size; if (cd9960_translate_node_common(newnode) == 0) return 0; /* Finally, overwrite some of the values that are set by default */ cd9660_time_915(node->inode->st.st_mtime, newnode->isoDirRecord->date); return 1; } /* * Compares two ISO filenames * @param const char * The first file name * @param const char * The second file name * @returns : -1 if first is less than second, 0 if they are the same, 1 if * the second is greater than the first */ static int cd9660_compare_filename(const char *first, const char *second) { /* * This can be made more optimal once it has been tested * (the extra character, for example, is for testing) */ int p1 = 0; int p2 = 0; char c1, c2; /* First, on the filename */ while (p1 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION-1 && p2 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION-1) { c1 = first[p1]; c2 = second[p2]; if (c1 == '.' && c2 =='.') break; else if (c1 == '.') { p2++; c1 = ' '; } else if (c2 == '.') { p1++; c2 = ' '; } else { p1++; p2++; } if (c1 < c2) return -1; else if (c1 > c2) { return 1; } } if (first[p1] == '.' && second[p2] == '.') { p1++; p2++; while (p1 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION - 1 && p2 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION - 1) { c1 = first[p1]; c2 = second[p2]; if (c1 == ';' && c2 == ';') break; else if (c1 == ';') { p2++; c1 = ' '; } else if (c2 == ';') { p1++; c2 = ' '; } else { p1++; p2++; } if (c1 < c2) return -1; else if (c1 > c2) return 1; } } return 0; } /* * Insert a node into list with ISO sorting rules * @param cd9660node * The head node of the list * @param cd9660node * The node to be inserted */ static void cd9660_sorted_child_insert(cd9660node *parent, cd9660node *cn_new) { int compare; cd9660node *cn; struct cd9660_children_head *head = &parent->cn_children; /* TODO: Optimize? */ cn_new->parent = parent; /* * first will either be 0, the . or the .. * if . or .., this means no other entry may be written before first * if 0, the new node may be inserted at the head */ TAILQ_FOREACH(cn, head, cn_next_child) { /* * Dont insert a node twice - * that would cause an infinite loop */ if (cn_new == cn) return; compare = cd9660_compare_filename(cn_new->isoDirRecord->name, cn->isoDirRecord->name); if (compare == 0) compare = cd9660_compare_filename(cn_new->node->name, cn->node->name); if (compare < 0) break; } if (cn == NULL) TAILQ_INSERT_TAIL(head, cn_new, cn_next_child); else TAILQ_INSERT_BEFORE(cn, cn_new, cn_next_child); } /* * Called After cd9660_sorted_child_insert * handles file collisions by suffixing each filname with ~n * where n represents the files respective place in the ordering */ static int cd9660_handle_collisions(cd9660node *colliding, int past) { cd9660node *iter, *next, *prev; int skip; int delete_chars = 0; int temp_past = past; int temp_skip; int flag = 0; cd9660node *end_of_range; for (iter = TAILQ_FIRST(&colliding->cn_children); iter != NULL && (next = TAILQ_NEXT(iter, cn_next_child)) != NULL;) { if (strcmp(iter->isoDirRecord->name, next->isoDirRecord->name) != 0) { iter = TAILQ_NEXT(iter, cn_next_child); continue; } flag = 1; temp_skip = skip = cd9660_count_collisions(iter); end_of_range = iter; while (temp_skip > 0) { temp_skip--; end_of_range = TAILQ_NEXT(end_of_range, cn_next_child); } temp_past = past; while (temp_past > 0) { if ((next = TAILQ_NEXT(end_of_range, cn_next_child)) != NULL) end_of_range = next; else if ((prev = TAILQ_PREV(iter, cd9660_children_head, cn_next_child)) != NULL) iter = prev; else delete_chars++; temp_past--; } skip += past; iter = cd9660_rename_filename(iter, skip, delete_chars); } return flag; } static cd9660node * cd9660_rename_filename(cd9660node *iter, int num, int delete_chars) { int i = 0; int numbts, dot, semi, digit, digits, temp, powers, multiplier, count; char *naming; int maxlength; char *tmp; if (diskStructure.verbose_level > 0) printf("Rename_filename called\n"); /* TODO : A LOT of chanes regarding 8.3 filenames */ if (diskStructure.isoLevel == 1) maxlength = 8; else if (diskStructure.isoLevel == 2) maxlength = 31; else maxlength = ISO_FILENAME_MAXLENGTH_BEFORE_VERSION; tmp = calloc(1, ISO_FILENAME_MAXLENGTH_WITH_PADDING); while (i < num) { powers = 1; count = 0; digits = 1; multiplier = 1; while (((int)(i / powers) ) >= 10) { digits++; powers = powers * 10; } naming = iter->o_name; /* while ((*naming != '.') && (*naming != ';')) { naming++; count++; } */ dot = -1; semi = -1; while (count < maxlength) { if (*naming == '.') dot = count; else if (*naming == ';') { semi = count; break; } naming++; count++; } if ((count + digits) < maxlength) numbts = count; else numbts = maxlength - (digits); numbts -= delete_chars; /* 8.3 rules - keep the extension, add before the dot */ /* * This code makes a bunch of assumptions. * See if you can spot them all :) */ /* if (diskStructure.isoLevel == 1) { numbts = 8 - digits - delete_chars; if (dot < 0) { } else { if (dot < 8) { memmove(&tmp[numbts],&tmp[dot],4); } } } */ /* (copying just the filename before the '.' */ memcpy(tmp, (iter->o_name), numbts); /* adding the appropriate number following the name */ temp = i; while (digits > 0) { digit = (int)(temp / powers); temp = temp - digit * powers; snprintf(&tmp[numbts], ISO_FILENAME_MAXLENGTH_WITH_PADDING - numbts, "%d", digit); digits--; numbts++; powers = powers / 10; } while ((*naming != ';') && (numbts < maxlength)) { tmp[numbts] = (*naming); naming++; numbts++; } tmp[numbts] = ';'; tmp[numbts+1] = '1'; tmp[numbts+2] = '\0'; /* * now tmp has exactly the identifier * we want so we'll copy it back to record */ memcpy((iter->isoDirRecord->name), tmp, numbts + 3); iter = TAILQ_NEXT(iter, cn_next_child); i++; } free(tmp); return iter; } /* Todo: Figure out why these functions are nec. */ static void cd9660_copy_filenames(cd9660node *node) { cd9660node *cn; if (TAILQ_EMPTY(&node->cn_children)) return; if (TAILQ_FIRST(&node->cn_children)->isoDirRecord == NULL) { debug_print_tree(diskStructure.rootNode, 0); exit(1); } TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) { cd9660_copy_filenames(cn); memcpy(cn->o_name, cn->isoDirRecord->name, ISO_FILENAME_MAXLENGTH_WITH_PADDING); } } static void cd9660_sorting_nodes(cd9660node *node) { cd9660node *cn; TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) cd9660_sorting_nodes(cn); cd9660_sort_nodes(node); } /* XXX Bubble sort. */ static void cd9660_sort_nodes(cd9660node *node) { cd9660node *cn, *next; do { TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) { if ((next = TAILQ_NEXT(cn, cn_next_child)) == NULL) return; else if (strcmp(next->isoDirRecord->name, cn->isoDirRecord->name) >= 0) continue; TAILQ_REMOVE(&node->cn_children, next, cn_next_child); TAILQ_INSERT_BEFORE(cn, next, cn_next_child); break; } } while (cn != NULL); } static int cd9660_count_collisions(cd9660node *copy) { int count = 0; cd9660node *iter, *next; for (iter = copy; (next = TAILQ_NEXT(iter, cn_next_child)) != NULL; iter = next) { if (cd9660_compare_filename(iter->isoDirRecord->name, next->isoDirRecord->name) == 0) count++; else return count; } #if 0 if ((next = TAILQ_NEXT(iter, cn_next_child)) != NULL) { printf("cd9660_recurse_on_collision: count is %i \n", count); compare = cd9660_compare_filename(iter->isoDirRecord->name, next->isoDirRecord->name); if (compare == 0) { count++; return cd9660_recurse_on_collision(next, count); } else return count; } #endif return count; } static cd9660node * cd9660_rrip_move_directory(cd9660node *dir) { char newname[9]; cd9660node *tfile; /* * This function needs to: * 1) Create an empty virtual file in place of the old directory * 2) Point the virtual file to the new directory * 3) Point the relocated directory to its old parent * 4) Move the directory specified by dir into rr_moved_dir, * and rename it to "diskStructure.rock_ridge_move_count" (as a string) */ /* First see if the moved directory even exists */ if (diskStructure.rr_moved_dir == NULL) { diskStructure.rr_moved_dir = cd9660_create_directory(ISO_RRIP_DEFAULT_MOVE_DIR_NAME, diskStructure.rootNode, dir); if (diskStructure.rr_moved_dir == NULL) return 0; } /* Create a file with the same ORIGINAL name */ tfile = cd9660_create_file(dir->node->name, dir->parent, dir); if (tfile == NULL) return NULL; /* * Because files get a trailing period and version appended, * we must retain the correct ORIGINAL name ourselves. */ cd9660_name_iso_dir_record(tfile->isoDirRecord, dir->isoDirRecord->name, dir->isoDirRecord->name_len[0]); diskStructure.rock_ridge_move_count++; snprintf(newname, sizeof(newname), "%08i", diskStructure.rock_ridge_move_count); /* Point to old parent */ dir->rr_real_parent = dir->parent; /* Place the placeholder file */ if (TAILQ_EMPTY(&dir->rr_real_parent->cn_children)) { TAILQ_INSERT_HEAD(&dir->rr_real_parent->cn_children, tfile, cn_next_child); } else { cd9660_sorted_child_insert(dir->rr_real_parent, tfile); } /* Point to new parent */ dir->parent = diskStructure.rr_moved_dir; /* Point the file to the moved directory */ tfile->rr_relocated = dir; /* Actually move the directory */ cd9660_sorted_child_insert(diskStructure.rr_moved_dir, dir); /* TODO: Inherit permissions / ownership (basically the entire inode) */ /* Set the new name */ cd9660_name_iso_dir_record(dir->isoDirRecord, newname, 8); return dir; } static int cd9660_add_dot_records(cd9660node *root) { struct cd9660_children_head *head = &root->cn_children; cd9660node *cn; TAILQ_FOREACH(cn, head, cn_next_child) { if ((cn->type & CD9660_TYPE_DIR) == 0) continue; /* Recursion first */ cd9660_add_dot_records(cn); } cd9660_create_special_directory(CD9660_TYPE_DOT, root); cd9660_create_special_directory(CD9660_TYPE_DOTDOT, root); return 1; } /* * Convert node to cd9660 structure * This function is designed to be called recursively on the root node of * the filesystem * Lots of recursion going on here, want to make sure it is efficient * @param struct fsnode * The root node to be converted * @param struct cd9660* The parent node (should not be NULL) * @param int Current directory depth * @param int* Running count of the number of directories that are being created */ static void cd9660_convert_structure(fsnode *root, cd9660node *parent_node, int level, int *numDirectories, int *error) { fsnode *iterator = root; cd9660node *this_node; int working_level; int add; int flag = 0; int counter = 0; /* * Newer, more efficient method, reduces recursion depth */ if (root == NULL) { warnx("%s: root is null\n", __func__); return; } /* Test for an empty directory - makefs still gives us the . record */ if ((S_ISDIR(root->type)) && (root->name[0] == '.') && (root->name[1] == '\0')) { root = root->next; if (root == NULL) return; } if ((this_node = cd9660_allocate_cd9660node()) == NULL) { CD9660_MEM_ALLOC_ERROR(__func__); } /* * To reduce the number of recursive calls, we will iterate over * the next pointers to the right. */ while (iterator != NULL) { add = 1; /* * Increment the directory count if this is a directory * Ignore "." entries. We will generate them later */ if (!S_ISDIR(iterator->type) || strcmp(iterator->name, ".") != 0) { /* Translate the node, including its filename */ this_node->parent = parent_node; cd9660_translate_node(iterator, this_node); this_node->level = level; if (S_ISDIR(iterator->type)) { (*numDirectories)++; this_node->type = CD9660_TYPE_DIR; working_level = level + 1; /* * If at level 8, directory would be at 8 * and have children at 9 which is not * allowed as per ISO spec */ if (level == 8) { if ((!diskStructure.allow_deep_trees) && (!diskStructure.rock_ridge_enabled)) { warnx("error: found entry " "with depth greater " "than 8."); (*error) = 1; return; } else if (diskStructure. rock_ridge_enabled) { working_level = 3; /* * Moved directory is actually * at level 2. */ this_node->level = working_level - 1; if (cd9660_rrip_move_directory( this_node) == 0) { warnx("Failure in " "cd9660_rrip_" "move_directory" ); (*error) = 1; return; } add = 0; } } /* Do the recursive call on the children */ if (iterator->child != 0) { cd9660_convert_structure( iterator->child, this_node, working_level, numDirectories, error); if ((*error) == 1) { warnx("%s: Error on recursive " "call", __func__); return; } } } else { /* Only directories should have children */ assert(iterator->child == NULL); this_node->type = CD9660_TYPE_FILE; } /* * Finally, do a sorted insert */ if (add) { cd9660_sorted_child_insert( parent_node, this_node); } /*Allocate new temp_node */ if (iterator->next != 0) { this_node = cd9660_allocate_cd9660node(); if (this_node == NULL) CD9660_MEM_ALLOC_ERROR(__func__); } } iterator = iterator->next; } /* cd9660_handle_collisions(first_node); */ /* TODO: need cleanup */ cd9660_copy_filenames(parent_node); do { flag = cd9660_handle_collisions(parent_node, counter); counter++; cd9660_sorting_nodes(parent_node); } while ((flag == 1) && (counter < 100)); } /* * Clean up the cd9660node tree * This is designed to be called recursively on the root node * @param struct cd9660node *root The node to free * @returns void */ static void cd9660_free_structure(cd9660node *root) { cd9660node *cn; while ((cn = TAILQ_FIRST(&root->cn_children)) != NULL) { TAILQ_REMOVE(&root->cn_children, cn, cn_next_child); cd9660_free_structure(cn); } free(root); } /* * Be a little more memory conservative: * instead of having the TAILQ_ENTRY as part of the cd9660node, * just create a temporary structure */ struct ptq_entry { TAILQ_ENTRY(ptq_entry) ptq; cd9660node *node; } *n; #define PTQUEUE_NEW(n,s,r,t){\ n = calloc(1, sizeof (struct s)); \ if (n == NULL) \ return r; \ n->node = t;\ } /* * Generate the path tables * The specific implementation of this function is left as an exercise to the * programmer. It could be done recursively. Make sure you read how the path * table has to be laid out, it has levels. * @param struct iso9660_disk *disk The disk image * @returns int The number of built path tables (between 1 and 4), 0 on failure */ static int cd9660_generate_path_table(void) { cd9660node *cn, *dirNode = diskStructure.rootNode; cd9660node *last = dirNode; int pathTableSize = 0; /* computed as we go */ int counter = 1; /* root gets a count of 0 */ int parentRecNum = 0; /* root's parent is '0' */ TAILQ_HEAD(cd9660_pt_head, ptq_entry) pt_head; TAILQ_INIT(&pt_head); PTQUEUE_NEW(n, ptq_entry, -1, diskStructure.rootNode); /* Push the root node */ TAILQ_INSERT_HEAD(&pt_head, n, ptq); /* Breadth-first traversal of file structure */ while (pt_head.tqh_first != 0) { n = pt_head.tqh_first; dirNode = n->node; TAILQ_REMOVE(&pt_head, pt_head.tqh_first, ptq); free(n); /* Update the size */ pathTableSize += ISO_PATHTABLE_ENTRY_BASESIZE + dirNode->isoDirRecord->name_len[0]+ (dirNode->isoDirRecord->name_len[0] % 2 == 0 ? 0 : 1); /* includes the padding bit */ dirNode->ptnumber=counter; if (dirNode != last) { last->ptnext = dirNode; dirNode->ptprev = last; } last = dirNode; parentRecNum = 1; if (dirNode->parent != 0) parentRecNum = dirNode->parent->ptnumber; /* Push children onto queue */ TAILQ_FOREACH(cn, &dirNode->cn_children, cn_next_child) { /* * Dont add the DOT and DOTDOT types to the path * table. */ if ((cn->type != CD9660_TYPE_DOT) && (cn->type != CD9660_TYPE_DOTDOT)) { if (S_ISDIR(cn->node->type)) { PTQUEUE_NEW(n, ptq_entry, -1, cn); TAILQ_INSERT_TAIL(&pt_head, n, ptq); } } } counter++; } return pathTableSize; } void cd9660_compute_full_filename(cd9660node *node, char *buf, size_t buflen, int level) { cd9660node *parent; parent = (node->rr_real_parent == NULL ? node->parent : node->rr_real_parent); if (parent != NULL) { cd9660_compute_full_filename(parent, buf, buflen, level + 1); strlcat(buf, node->node->name, buflen); } else { /* We are at the root */ strlcat(buf, diskStructure.rootFilesystemPath, buflen); if (buf[strlen(buf) - 1] == '/') buf[strlen(buf) - 1] = '\0'; } if (level != 0) strlcat(buf, "/", buflen); } /* NEW filename conversion method */ typedef int(*cd9660_filename_conversion_functor)(const char *, char *, size_t, int); /* * TODO: These two functions are almost identical. * Some code cleanup is possible here * * XXX bounds checking! */ static int cd9660_level1_convert_filename(const char *oldname, char *newname, size_t namsz, int is_file) { /* * ISO 9660 : 10.1 * File Name shall not contain more than 8 d or d1 characters * File Name Extension shall not contain more than 3 d or d1 characters * Directory Identifier shall not contain more than 8 d or d1 characters */ int namelen = 0; int extlen = 0; int found_ext = 0; char *newname_ = newname; while (*oldname != '\0') { /* Handle period first, as it is special */ if (*oldname == '.') { if (found_ext) { *newname++ = '_'; extlen ++; } else { *newname++ = '.'; found_ext = 1; } } else { /* cut RISC OS file type off ISO name */ if (diskStructure.archimedes_enabled && *oldname == ',' && strlen(oldname) == 4) break; /* Enforce 12.3 / 8 */ if (((namelen == 8) && !found_ext) || (found_ext && extlen == 3)) { break; } if (islower((unsigned char)*oldname)) *newname++ = toupper((unsigned char)*oldname); else if (isupper((unsigned char)*oldname) || isdigit((unsigned char)*oldname)) *newname++ = *oldname; else *newname++ = '_'; if (found_ext) extlen++; else namelen++; } oldname ++; } if (is_file) { if (!found_ext && !diskStructure.omit_trailing_period) *newname++ = '.'; /* Add version */ snprintf(newname, namsz - (newname - newname_), ";%i", 1); } return namelen + extlen + found_ext; } /* XXX bounds checking! */ static int cd9660_level2_convert_filename(const char *oldname, char *newname, size_t namsz, int is_file) { /* * ISO 9660 : 7.5.1 * File name : 0+ d or d1 characters * separator 1 (.) * File name extension : 0+ d or d1 characters * separator 2 (;) * File version number (5 characters, 1-32767) * 1 <= Sum of File name and File name extension <= 30 */ int namelen = 0; int extlen = 0; int found_ext = 0; char *newname_ = newname; while (*oldname != '\0') { /* Handle period first, as it is special */ if (*oldname == '.') { if (found_ext) { if (diskStructure.allow_multidot) { *newname++ = '.'; } else { *newname++ = '_'; } extlen ++; } else { *newname++ = '.'; found_ext = 1; } } else { /* cut RISC OS file type off ISO name */ if (diskStructure.archimedes_enabled && *oldname == ',' && strlen(oldname) == 4) break; if ((namelen + extlen) == 30) break; if (islower((unsigned char)*oldname)) *newname++ = toupper((unsigned char)*oldname); else if (isupper((unsigned char)*oldname) || isdigit((unsigned char)*oldname)) *newname++ = *oldname; else if (diskStructure.allow_multidot && *oldname == '.') { *newname++ = '.'; } else { *newname++ = '_'; } if (found_ext) extlen++; else namelen++; } oldname ++; } if (is_file) { if (!found_ext && !diskStructure.omit_trailing_period) *newname++ = '.'; /* Add version */ snprintf(newname, namsz - (newname - newname_), ";%i", 1); } return namelen + extlen + found_ext; } #if 0 static int cd9660_joliet_convert_filename(const char *oldname, char *newname, size_t namsz, int is_file) { /* TODO: implement later, move to cd9660_joliet.c ?? */ } #endif /* * Convert a file name to ISO compliant file name * @param char * oldname The original filename * @param char ** newname The new file name, in the appropriate character * set and of appropriate length * @param int 1 if file, 0 if directory * @returns int The length of the new string */ static int cd9660_convert_filename(const char *oldname, char *newname, size_t namsz, int is_file) { /* NEW */ cd9660_filename_conversion_functor conversion_function = 0; if (diskStructure.isoLevel == 1) conversion_function = &cd9660_level1_convert_filename; else if (diskStructure.isoLevel == 2) conversion_function = &cd9660_level2_convert_filename; return (*conversion_function)(oldname, newname, namsz, is_file); } int cd9660_compute_record_size(cd9660node *node) { int size = node->isoDirRecord->length[0]; if (diskStructure.rock_ridge_enabled) size += node->susp_entry_size; size += node->su_tail_size; size += size & 1; /* Ensure length of record is even. */ assert(size <= 254); return size; } static void cd9660_populate_dot_records(cd9660node *node) { node->dot_record->fileDataSector = node->fileDataSector; memcpy(node->dot_record->isoDirRecord,node->isoDirRecord, 34); node->dot_record->isoDirRecord->name_len[0] = 1; node->dot_record->isoDirRecord->name[0] = 0; node->dot_record->isoDirRecord->name[1] = 0; node->dot_record->isoDirRecord->length[0] = 34; node->dot_record->fileRecordSize = cd9660_compute_record_size(node->dot_record); if (node == diskStructure.rootNode) { node->dot_dot_record->fileDataSector = node->fileDataSector; memcpy(node->dot_dot_record->isoDirRecord,node->isoDirRecord, 34); } else { node->dot_dot_record->fileDataSector = node->parent->fileDataSector; memcpy(node->dot_dot_record->isoDirRecord, node->parent->isoDirRecord,34); } node->dot_dot_record->isoDirRecord->name_len[0] = 1; node->dot_dot_record->isoDirRecord->name[0] = 1; node->dot_dot_record->isoDirRecord->name[1] = 0; node->dot_dot_record->isoDirRecord->length[0] = 34; node->dot_dot_record->fileRecordSize = cd9660_compute_record_size(node->dot_dot_record); } /* * @param struct cd9660node *node The node * @param int The offset (in bytes) - SHOULD align to the beginning of a sector * @returns int The total size of files and directory entries (should be * a multiple of sector size) */ static int cd9660_compute_offsets(cd9660node *node, int startOffset) { /* * This function needs to compute the size of directory records and * runs, file lengths, and set the appropriate variables both in * cd9660node and isoDirEntry */ int used_bytes = 0; int current_sector_usage = 0; cd9660node *child; fsinode *inode; int r; assert(node != NULL); /* * NOTE : There needs to be some special case detection for * the "real root" node, since for it, node->node is undefined */ node->fileDataSector = -1; if (node->type & CD9660_TYPE_DIR) { node->fileRecordSize = cd9660_compute_record_size(node); /*Set what sector this directory starts in*/ node->fileDataSector = CD9660_BLOCKS(diskStructure.sectorSize,startOffset); cd9660_bothendian_dword(node->fileDataSector, node->isoDirRecord->extent); /* * First loop over children, need to know the size of * their directory records */ node->fileSectorsUsed = 1; TAILQ_FOREACH(child, &node->cn_children, cn_next_child) { node->fileDataLength += cd9660_compute_record_size(child); if ((cd9660_compute_record_size(child) + current_sector_usage) >= diskStructure.sectorSize) { current_sector_usage = 0; node->fileSectorsUsed++; } current_sector_usage += cd9660_compute_record_size(child); } cd9660_bothendian_dword(node->fileSectorsUsed * diskStructure.sectorSize,node->isoDirRecord->size); /* * This should point to the sector after the directory * record (or, the first byte in that sector) */ used_bytes += node->fileSectorsUsed * diskStructure.sectorSize; for (child = TAILQ_NEXT(node->dot_dot_record, cn_next_child); child != NULL; child = TAILQ_NEXT(child, cn_next_child)) { /* Directories need recursive call */ if (S_ISDIR(child->node->type)) { r = cd9660_compute_offsets(child, used_bytes + startOffset); if (r != -1) used_bytes += r; else return -1; } } /* Explicitly set the . and .. records */ cd9660_populate_dot_records(node); /* Finally, do another iteration to write the file data*/ for (child = TAILQ_NEXT(node->dot_dot_record, cn_next_child); child != NULL; child = TAILQ_NEXT(child, cn_next_child)) { /* Files need extent set */ if (S_ISDIR(child->node->type)) continue; child->fileRecordSize = cd9660_compute_record_size(child); child->fileSectorsUsed = CD9660_BLOCKS(diskStructure.sectorSize, child->fileDataLength); inode = child->node->inode; if ((inode->flags & FI_ALLOCATED) == 0) { inode->ino = CD9660_BLOCKS(diskStructure.sectorSize, used_bytes + startOffset); inode->flags |= FI_ALLOCATED; used_bytes += child->fileSectorsUsed * diskStructure.sectorSize; } else { INODE_WARNX(("%s: already allocated inode %d " "data sectors at %" PRIu32, __func__, (int)inode->st.st_ino, inode->ino)); } child->fileDataSector = inode->ino; cd9660_bothendian_dword(child->fileDataSector, child->isoDirRecord->extent); } } return used_bytes; } #if 0 /* Might get rid of this func */ static int cd9660_copy_stat_info(cd9660node *from, cd9660node *to, int file) { to->node->inode->st.st_dev = 0; to->node->inode->st.st_ino = 0; to->node->inode->st.st_size = 0; to->node->inode->st.st_blksize = from->node->inode->st.st_blksize; to->node->inode->st.st_atime = from->node->inode->st.st_atime; to->node->inode->st.st_mtime = from->node->inode->st.st_mtime; to->node->inode->st.st_ctime = from->node->inode->st.st_ctime; to->node->inode->st.st_uid = from->node->inode->st.st_uid; to->node->inode->st.st_gid = from->node->inode->st.st_gid; to->node->inode->st.st_mode = from->node->inode->st.st_mode; /* Clear out type */ to->node->inode->st.st_mode = to->node->inode->st.st_mode & ~(S_IFMT); if (file) to->node->inode->st.st_mode |= S_IFREG; else to->node->inode->st.st_mode |= S_IFDIR; return 1; } #endif static cd9660node * cd9660_create_virtual_entry(const char *name, cd9660node *parent, int file, int insert) { cd9660node *temp; fsnode * tfsnode; assert(parent != NULL); temp = cd9660_allocate_cd9660node(); if (temp == NULL) return NULL; if ((tfsnode = calloc(1, sizeof (fsnode))) == NULL) { CD9660_MEM_ALLOC_ERROR("cd9660_create_virtual_entry"); return NULL; } /* Assume for now name is a valid length */ if ((tfsnode->name = strdup(name)) == NULL) { CD9660_MEM_ALLOC_ERROR("cd9660_create_virtual_entry"); return NULL; } if ((temp->isoDirRecord = calloc(1, sizeof (iso_directory_record_cd9660))) == NULL) { CD9660_MEM_ALLOC_ERROR("cd9660_create_virtual_entry"); return NULL; } cd9660_convert_filename(tfsnode->name, temp->isoDirRecord->name, sizeof (temp->isoDirRecord->name), file); temp->node = tfsnode; temp->parent = parent; if (insert) { if (temp->parent != NULL) { temp->level = temp->parent->level + 1; if (!TAILQ_EMPTY(&temp->parent->cn_children)) cd9660_sorted_child_insert(temp->parent, temp); else TAILQ_INSERT_HEAD(&temp->parent->cn_children, temp, cn_next_child); } } if (parent->node != NULL) { tfsnode->type = parent->node->type; } /* Clear out file type bits */ tfsnode->type &= ~(S_IFMT); if (file) tfsnode->type |= S_IFREG; else tfsnode->type |= S_IFDIR; /* Indicate that there is no spec entry (inode) */ tfsnode->flags &= ~(FSNODE_F_HASSPEC); #if 0 cd9660_copy_stat_info(parent, temp, file); #endif return temp; } static cd9660node * cd9660_create_file(const char * name, cd9660node *parent, cd9660node *me) { cd9660node *temp; temp = cd9660_create_virtual_entry(name,parent,1,1); if (temp == NULL) return NULL; temp->fileDataLength = 0; temp->type = CD9660_TYPE_FILE | CD9660_TYPE_VIRTUAL; if ((temp->node->inode = calloc(1, sizeof(fsinode))) == NULL) return NULL; *temp->node->inode = *me->node->inode; if (cd9960_translate_node_common(temp) == 0) return NULL; return temp; } /* * Create a new directory which does not exist on disk * @param const char * name The name to assign to the directory * @param const char * parent Pointer to the parent directory * @returns cd9660node * Pointer to the new directory */ static cd9660node * cd9660_create_directory(const char *name, cd9660node *parent, cd9660node *me) { cd9660node *temp; temp = cd9660_create_virtual_entry(name,parent,0,1); if (temp == NULL) return NULL; temp->node->type |= S_IFDIR; temp->type = CD9660_TYPE_DIR | CD9660_TYPE_VIRTUAL; if ((temp->node->inode = calloc(1, sizeof(fsinode))) == NULL) return NULL; *temp->node->inode = *me->node->inode; if (cd9960_translate_node_common(temp) == 0) return NULL; return temp; } static cd9660node * cd9660_create_special_directory(u_char type, cd9660node *parent) { cd9660node *temp, *first; char na[2]; assert(parent != NULL); if (type == CD9660_TYPE_DOT) na[0] = 0; else if (type == CD9660_TYPE_DOTDOT) na[0] = 1; else return 0; na[1] = 0; if ((temp = cd9660_create_virtual_entry(na, parent, 0, 0)) == NULL) return NULL; temp->parent = parent; temp->type = type; temp->isoDirRecord->length[0] = 34; /* Dot record is always first */ if (type == CD9660_TYPE_DOT) { parent->dot_record = temp; TAILQ_INSERT_HEAD(&parent->cn_children, temp, cn_next_child); /* DotDot should be second */ } else if (type == CD9660_TYPE_DOTDOT) { parent->dot_dot_record = temp; /* * If the first child is the dot record, insert * this second. Otherwise, insert it at the head. */ if ((first = TAILQ_FIRST(&parent->cn_children)) == NULL || (first->type & CD9660_TYPE_DOT) == 0) { TAILQ_INSERT_HEAD(&parent->cn_children, temp, cn_next_child); } else { TAILQ_INSERT_AFTER(&parent->cn_children, first, temp, cn_next_child); } } return temp; } int cd9660_add_generic_bootimage(const char *bootimage) { struct stat stbuf; assert(bootimage != NULL); if (*bootimage == '\0') { warnx("Error: Boot image must be a filename"); return 0; } if ((diskStructure.generic_bootimage = strdup(bootimage)) == NULL) { warn("%s: strdup", __func__); return 0; } /* Get information about the file */ if (lstat(diskStructure.generic_bootimage, &stbuf) == -1) err(EXIT_FAILURE, "%s: lstat(\"%s\")", __func__, diskStructure.generic_bootimage); if (stbuf.st_size > 32768) { warnx("Error: Boot image must be no greater than 32768 bytes"); return 0; } if (diskStructure.verbose_level > 0) { printf("Generic boot image image has size %lld\n", (long long)stbuf.st_size); } diskStructure.has_generic_bootimage = 1; return 1; } makefs/src/usr.sbin/makefs/cd9660.h010064400000000000000000000336361223453422700141560ustar00/** $MirOS: src/usr.sbin/makefs/cd9660.h,v 1.18 2013/10/31 20:07:26 tg Exp $ */ /* $NetBSD: cd9660.h,v 1.13 2009/01/10 22:06:29 bjh21 Exp $ */ /* * Copyright (c) 2009, 2010, 2013 * Thorsten Glaser * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan * Perez-Rathke and Ram Vedam. All rights reserved. * * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, * Alan Perez-Rathke and Ram Vedam. * * 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 DANIEL WATT, WALTER DEIGNAN, RYAN * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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 _MAKEFS_CD9660_H #define _MAKEFS_CD9660_H #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "makefs.h" #include "iso.h" #include "iso_rrip.h" #include "cd9660/cd9660_eltorito.h" #ifdef DEBUG #define INODE_WARNX(__x) warnx __x #else /* DEBUG */ #define INODE_WARNX(__x) #endif /* DEBUG */ #undef __bounded #if defined(__GNUC__) && (defined(__OpenBSD__) || defined(__MirBSD__)) #define __bounded(...) __attribute__((__bounded__(__VA_ARGS__))) #else #define __bounded(...) /* nothing */ #endif /* prototype with bounds checking */ #if 0 /* defined(__GNUC__) && (defined(__OpenBSD__) || defined(__MirBSD__)) */ /* Anil Madhavapeddy's gcc bounds checker, doesn't trigger */ #define cd9660_DATATYPE_PROTO(name, bytes, type) \ void __CONCAT(cd9660_real_, name)(type, unsigned char *) __attribute__((__bounded__(__minbytes__, 2, bytes))) #define cd9660_DATATYPE_INVOCATION(name, bytes, val, buf) \ __CONCAT(cd9660_real_, name)(val, (unsigned char *)(buf)) #elif defined(DEBUG) /* compile-time assertion */ #define cd9660_DATATYPE_PROTO(name, bytes, type) \ void __CONCAT(cd9660_real_, name)(type, unsigned char *) #define cd9660_DATATYPE_INVOCATION(name, bytes, val, buf) do { \ int cd9660_CHECK[sizeof (buf) >= bytes ? 1 : -1] __unused; \ __CONCAT(cd9660_real_, name)(val, (unsigned char *)(buf)); \ } while (/* CONSTCOND */ 0) #else /* run-time assertion */ #define cd9660_DATATYPE_PROTO(name, bytes, type) \ void __CONCAT(cd9660_real_, name)(type, unsigned char *) #define cd9660_DATATYPE_INVOCATION(name, bytes, val, buf) do { \ assert(sizeof (buf) >= bytes); \ __CONCAT(cd9660_real_, name)(val, (unsigned char *)(buf)); \ } while (/* CONSTCOND */ 0) #endif #ifdef CD9660_CONVERSION_IMPL #undef cd9660_DATATYPE_INVOCATION #define cd9660_DATATYPE_INVOCATION(name, bytes, val, buf) \ __CONCAT(cd9660_real_, name)(val, buf) #endif #define CD9660MAXPATH 4096 #define ISO_STRING_FILTER_NONE = 0x00 #define ISO_STRING_FILTER_DCHARS = 0x01 #define ISO_STRING_FILTER_ACHARS = 0x02 /* Extended preferences type, in the spirit of what makefs gives us (only ints) */ typedef struct { const char *shortName; /* Short option */ const char *name; /* option name */ char *value; /* where to stuff the value */ int minLength; /* minimum for value */ int maxLength; /* maximum for value */ const char *desc; /* option description */ int filterFlags; } string_option_t; /******** STRUCTURES **********/ /*Defaults*/ #define ISO_DEFAULT_VOLUMEID "MAKEFS_CD9660_IMAGE" #define ISO_DEFAULT_APPID "MAKEFS" #define ISO_DEFAULT_PUBLISHER "MAKEFS" #define ISO_DEFAULT_PREPARER "MAKEFS" #ifdef __MirBSD__ #define ISO_DEFAULT_SYSID "MirBSD" #else #define ISO_DEFAULT_SYSID "NetBSD" #endif #define ISO_VOLUME_DESCRIPTOR_STANDARD_ID "CD001" #define ISO_VOLUME_DESCRIPTOR_BOOT 0 #define ISO_VOLUME_DESCRIPTOR_PVD 1 #define ISO_VOLUME_DESCRIPTOR_TERMINATOR 255 /*30 for name and extension, as well as version number and padding bit*/ #define ISO_FILENAME_MAXLENGTH_BEFORE_VERSION 30 #define ISO_FILENAME_MAXLENGTH 36 #define ISO_FILENAME_MAXLENGTH_WITH_PADDING 37 #define ISO_FLAG_CLEAR 0x00 #define ISO_FLAG_HIDDEN 0x01 #define ISO_FLAG_DIRECTORY 0x02 #define ISO_FLAG_ASSOCIATED 0x04 #define ISO_FLAG_PERMISSIONS 0x08 #define ISO_FLAG_RESERVED5 0x10 #define ISO_FLAG_RESERVED6 0x20 #define ISO_FLAG_FINAL_RECORD 0x40 #define ISO_PATHTABLE_ENTRY_BASESIZE 8 #define ISO_RRIP_DEFAULT_MOVE_DIR_NAME "RR_MOVED" #define RRIP_DEFAULT_MOVE_DIR_NAME \ (diskStructure.hide_rr_moved ? "" : ".rr_moved") #define CD9660_BLOCKS(__sector_size, __bytes) \ howmany((__bytes), (__sector_size)) #define CD9660_MEM_ALLOC_ERROR(_F) \ err(EXIT_FAILURE, "%s, %s l. %d", _F, __FILE__, __LINE__) #define CD9660_IS_COMMAND_ARG_DUAL(var,short,long)\ (strcmp((var),(short)) == 0) || (strcmp((var),(long))==0) #define CD9660_IS_COMMAND_ARG(var,arg)\ (strcmp((var),(arg)) == 0) #define CD9660_TYPE_FILE 0x01 #define CD9660_TYPE_DIR 0x02 #define CD9660_TYPE_DOT 0x04 #define CD9660_TYPE_DOTDOT 0x08 #define CD9660_TYPE_VIRTUAL 0x80 #define CD9660_INODE_HASH_SIZE 1024 #define CD9660_END_PADDING 150 /* Slight modification of the ISO structure in iso.h */ typedef struct _iso_directory_record_cd9660 { u_char length [ISODCL (1, 1)]; /* 711 */ u_char ext_attr_length [ISODCL (2, 2)]; /* 711 */ u_char extent [ISODCL (3, 10)]; /* 733 */ u_char size [ISODCL (11, 18)]; /* 733 */ u_char date [ISODCL (19, 25)]; /* 7 by 711 */ u_char flags [ISODCL (26, 26)]; u_char file_unit_size [ISODCL (27, 27)]; /* 711 */ u_char interleave [ISODCL (28, 28)]; /* 711 */ u_char volume_sequence_number [ISODCL (29, 32)]; /* 723 */ u_char name_len [ISODCL (33, 33)]; /* 711 */ char name [ISO_FILENAME_MAXLENGTH_WITH_PADDING]; } iso_directory_record_cd9660; /* TODO: Lots of optimization of this structure */ typedef struct _cd9660node { u_char type;/* Used internally */ /* Tree structure */ struct _cd9660node *parent; /* parent (NULL if root) */ TAILQ_HEAD(cd9660_children_head, _cd9660node) cn_children; TAILQ_ENTRY(_cd9660node) cn_next_child; struct _cd9660node *dot_record; /* For directories, used mainly in RRIP */ struct _cd9660node *dot_dot_record; fsnode *node; /* pointer to fsnode */ struct _iso_directory_record_cd9660 *isoDirRecord; struct iso_extended_attributes *isoExtAttributes; /***** SIZE CALCULATION *****/ /*already stored in isoDirRecord, but this is an int version, and will be copied to isoDirRecord on writing*/ uint32_t fileDataSector; /* * same thing, though some notes: * If a file, this is the file size * If a directory, this is the size of all its children's * directory records * plus necessary padding */ int64_t fileDataLength; /* * XXXfvdl sectors are int */ int fileSectorsUsed; int fileRecordSize;/*copy of a variable, int for quicker calculations*/ /* Old name, used for renaming - needs to be optimized but low priority */ char o_name [ISO_FILENAME_MAXLENGTH_WITH_PADDING]; /***** SPACE RESERVED FOR EXTENSIONS *****/ /* For memory efficiency's sake - we should move this to a separate struct and point to null if not needed */ /* For Rock Ridge */ struct _cd9660node *rr_real_parent, *rr_relocated; int susp_entry_size; int susp_dot_entry_size; int susp_dot_dot_entry_size; /* Continuation area stuff */ int susp_entry_ce_start; int susp_dot_ce_start; int susp_dot_dot_ce_start; int susp_entry_ce_length; int susp_dot_ce_length; int susp_dot_dot_ce_length; /* Data to put at the end of the System Use field */ int su_tail_size; char *su_tail_data; /*** PATH TABLE STUFF ***/ int level; /*depth*/ int ptnumber; struct _cd9660node *ptnext, *ptprev, *ptlast; /* SUSP entries */ TAILQ_HEAD(susp_linked_list, ISO_SUSP_ATTRIBUTES) head; } cd9660node; typedef struct _path_table_entry { u_char length[ISODCL (1, 1)]; u_char extended_attribute_length[ISODCL (2, 2)]; u_char first_sector[ISODCL (3, 6)]; u_char parent_number[ISODCL (7, 8)]; u_char name[ISO_FILENAME_MAXLENGTH_WITH_PADDING]; } path_table_entry; typedef struct _volume_descriptor { u_char *volumeDescriptorData; /*ALWAYS 2048 bytes long*/ int sector; struct _volume_descriptor *next; } volume_descriptor; typedef struct _iso9660_disk { int sectorSize; struct iso_primary_descriptor primaryDescriptor; struct iso_supplementary_descriptor supplementaryDescriptor; volume_descriptor *firstVolumeDescriptor; cd9660node *rootNode; const char *rootFilesystemPath; /* Important sector numbers here */ /* primaryDescriptor.type_l_path_table*/ int primaryBigEndianTableSector; /* primaryDescriptor.type_m_path_table*/ int primaryLittleEndianTableSector; /* primaryDescriptor.opt_type_l_path_table*/ int secondaryBigEndianTableSector; /* primaryDescriptor.opt_type_m_path_table*/ int secondaryLittleEndianTableSector; /* primaryDescriptor.path_table_size*/ int pathTableLength; int dataFirstSector; int totalSectors; /* OPTIONS GO HERE */ int isoLevel; int include_padding_areas; int follow_sym_links; int verbose_level; int displayHelp; int keep_bad_images; int hide_rr_moved; /*XXX why int and not bool? */ /* SUSP options and variables */ int susp_continuation_area_start_sector; int susp_continuation_area_size; int susp_continuation_area_current_free; int rock_ridge_enabled; /* Other Rock Ridge Variables */ char *rock_ridge_renamed_dir_name; int rock_ridge_move_count; cd9660node *rr_moved_dir; int archimedes_enabled; /* Spec breaking options */ u_char allow_deep_trees; u_char allow_start_dot; u_char allow_max_name; /* Allow 37 char filenames*/ u_char allow_illegal_chars; /* ~, !, # */ u_char allow_lowercase; u_char allow_multidot; u_char omit_trailing_period; /* BOOT INFORMATION HERE */ int has_generic_bootimage; /* Default to 0 */ char *generic_bootimage; int is_bootable;/* Default to 0 */ int boot_catalog_sector; boot_volume_descriptor *boot_descriptor; char * boot_image_directory; TAILQ_HEAD(boot_image_list,cd9660_boot_image) boot_images; int image_serialno; LIST_HEAD(boot_catalog_entries,boot_catalog_entry) boot_entries; char *forced_creation_date; char *forced_modification_date; char *forced_expiration_date; char *forced_effective_date; } iso9660_disk; /******** GLOBAL VARIABLES ***********/ extern iso9660_disk diskStructure; /************ FUNCTIONS **************/ int cd9660_valid_a_chars(const char *); int cd9660_valid_d_chars(const char *); void cd9660_uppercase_characters(char *, int); /* ISO Data Types */ #define cd9660_721(val, buf) \ cd9660_DATATYPE_INVOCATION(721, 2, val, buf) cd9660_DATATYPE_PROTO(721, 2, uint16_t); #define cd9660_731(val, buf) \ cd9660_DATATYPE_INVOCATION(731, 4, val, buf) cd9660_DATATYPE_PROTO(731, 4, uint32_t); #define cd9660_722(val, buf) \ cd9660_DATATYPE_INVOCATION(722, 2, val, buf) cd9660_DATATYPE_PROTO(722, 2, uint16_t); #define cd9660_732(val, buf) \ cd9660_DATATYPE_INVOCATION(732, 4, val, buf) cd9660_DATATYPE_PROTO(732, 4, uint32_t); #define cd9660_bothendian_dword(val, buf) \ cd9660_DATATYPE_INVOCATION(bothendian_dword, 8, val, buf) cd9660_DATATYPE_PROTO(bothendian_dword, 8, uint32_t); #define cd9660_bothendian_word(val, buf) \ cd9660_DATATYPE_INVOCATION(bothendian_word, 4, val, buf) cd9660_DATATYPE_PROTO(bothendian_word, 4, uint16_t); #if 0 #define cd9660_set_date(val, buf) \ cd9660_DATATYPE_INVOCATION(set_date, ?, val, buf) cd9660_DATATYPE_PROTO(set_date, ?, time_t); #endif #define cd9660_time_8426(val, buf) \ cd9660_DATATYPE_INVOCATION(time_8426, 17, val, buf) cd9660_DATATYPE_PROTO(time_8426, 17, time_t); #define cd9660_time_915(val, buf) \ cd9660_DATATYPE_INVOCATION(time_915, 7, val, buf) cd9660_DATATYPE_PROTO(time_915, 7, time_t); int cd9660_isthisa_time_8426_utc(const char *, const char *); /*** Boot Functions ***/ int cd9660_write_generic_bootimage(FILE *); int cd9660_add_generic_bootimage(const char *); int cd9660_write_boot(FILE *); int cd9660_add_boot_disk(const char *); int cd9660_eltorito_add_boot_option(const char *, const char *); int cd9660_setup_boot(int); int cd9660_setup_boot_volume_descriptor(volume_descriptor *); /*** Write Functions ***/ int cd9660_write_image(const char *image); int cd9660_copy_file(FILE *, int, const char *); void cd9660_compute_full_filename(cd9660node *, char *, size_t, int) __bounded(string, 2, 3); int cd9660_compute_record_size(cd9660node *); /* Debugging functions */ void debug_print_tree(cd9660node *,int); void debug_print_path_tree(cd9660node *); void debug_print_volume_descriptor_information(void); void debug_dump_to_xml_ptentry(path_table_entry *,int, int); void debug_dump_to_xml_path_table(FILE *, int, int, int); void debug_dump_to_xml(FILE *); int debug_get_encoded_number(unsigned char *, int); void debug_dump_integer(const char *, char *,int); void debug_dump_string(const char *,unsigned char *,int); void debug_dump_directory_record_9_1(unsigned char *); void debug_dump_to_xml_volume_descriptor(unsigned char *,int); #define cd9660_pad_string_spaces(x) \ cd9660_pad_string_spaces_((x), sizeof (x)) void cd9660_pad_string_spaces_(char *, size_t); #endif makefs/src/usr.sbin/makefs/cd9660/Makefile.inc010064400000000000000000000005771134454447200162170ustar00# $MirOS: src/usr.sbin/makefs/cd9660/Makefile.inc,v 1.2 2010/03/06 21:29:06 tg Exp $ # $NetBSD: Makefile.inc,v 1.1 2009/01/16 19:39:52 pooka Exp $ # .PATH: ${.CURDIR}/cd9660 ${NETBSDSRCDIR}/sys/fs/cd9660 CPPFLAGS+=-I${NETBSDSRCDIR}/sys/fs/cd9660 SRCS+= cd9660_strings.c cd9660_debug.c cd9660_eltorito.c SRCS+= cd9660_write.c cd9660_conversion.c iso9660_rrip.c cd9660_archimedes.c makefs/src/usr.sbin/makefs/cd9660/cd9660_archimedes.c010064400000000000000000000102061134453732000172320ustar00/* $NetBSD: cd9660_archimedes.c,v 1.1 2009/01/10 22:06:29 bjh21 Exp $ */ /*- * Copyright (c) 1998, 2009 Ben Harris * 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. */ /* * cd9660_archimedes.c - support for RISC OS "ARCHIMEDES" extension * * RISC OS CDFS looks for a special block at the end of the System Use * Field for each file. If present, this contains the RISC OS load * and exec address (used to hold the file timestamp and type), the * file attributes, and a flag indicating whether the first character * of the filename should be replaced with '!' (since many special * RISC OS filenames do). */ #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #include #if defined(__RCSID) && !defined(__lint) __RCSID("$NetBSD: cd9660_archimedes.c,v 1.1 2009/01/10 22:06:29 bjh21 Exp $"); #endif /* !__lint */ #include #include #include #include #include "makefs.h" #include "cd9660.h" #include "cd9660_archimedes.h" /* * Convert a Unix time_t (non-leap seconds since 1970-01-01) to a RISC * OS time (non-leap(?) centiseconds since 1900-01-01(?)). */ static u_int64_t riscos_date(time_t unixtime) { u_int64_t base; base = 31536000ULL * 70 + 86400 * 17; return (((u_int64_t)unixtime) + base)*100; } /* * Add "ARCHIMEDES" metadata to a node if that seems appropriate. * * We touch regular files with names matching /,[0-9a-f]{3}$/ and * directories matching /^!/. */ static void archimedes_convert_node(cd9660node *node) { struct ISO_ARCHIMEDES *arc; size_t len; int type = -1; uint64_t stamp; if (node->su_tail_data != NULL) /* Something else already has the tail. */ return; len = strlen(node->node->name); if (len < 1) return; if (len >= 4 && node->node->name[len-4] == ',') /* XXX should support ,xxx and ,lxa */ type = strtoul(node->node->name + len - 3, NULL, 16); if (type == -1 && node->node->name[0] != '!') return; if (type == -1) type = 0; assert(sizeof(struct ISO_ARCHIMEDES) == 32); if ((arc = calloc(1, sizeof(struct ISO_ARCHIMEDES))) == NULL) { CD9660_MEM_ALLOC_ERROR("archimedes_convert_node"); exit(1); } stamp = riscos_date(node->node->inode->st.st_mtime); memcpy(arc->magic, "ARCHIMEDES", 10); cd9660_731(0xfff00000 | (type << 8) | (stamp >> 32), arc->loadaddr); cd9660_731(stamp & 0x00ffffffffULL, arc->execaddr); arc->ro_attr = RO_ACCESS_UR | RO_ACCESS_OR; arc->cdfs_attr = node->node->name[0] == '!' ? CDFS_PLING : 0; node->su_tail_data = (void *)arc; node->su_tail_size = sizeof(*arc); } /* * Add "ARCHIMEDES" metadata to an entire tree recursively. */ void archimedes_convert_tree(cd9660node *node) { cd9660node *cn; assert(node != NULL); archimedes_convert_node(node); /* Recurse on children. */ TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) archimedes_convert_tree(cn); } makefs/src/usr.sbin/makefs/cd9660/cd9660_archimedes.h010064400000000000000000000043251134453732000172440ustar00/* $NetBSD: cd9660_archimedes.h,v 1.1 2009/01/10 22:06:29 bjh21 Exp $ */ /*- * Copyright (c) 1998, 2009 Ben Harris * 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. */ /* * cd9660_archimedes.c - support for RISC OS "ARCHIMEDES" extension */ struct ISO_ARCHIMEDES { char magic[10]; /* "ARCHIMEDES" */ unsigned char loadaddr[4]; /* Load address, little-endian */ unsigned char execaddr[4]; /* Exec address, little-endian */ unsigned char ro_attr; /* RISC OS attributes */ #define RO_ACCESS_UR 0x01 /* Owner read */ #define RO_ACCESS_UW 0x02 /* Owner write */ #define RO_ACCESS_L 0x04 /* Locked */ #define RO_ACCESS_OR 0x10 /* Public read */ #define RO_ACCESS_OW 0x20 /* Public write */ unsigned char cdfs_attr; /* Extra attributes for CDFS */ #define CDFS_PLING 0x01 /* Filename begins with '!' */ char reserved[12]; }; extern void archimedes_convert_tree(cd9660node *); makefs/src/usr.sbin/makefs/cd9660/cd9660_conversion.c010064400000000000000000000150671134455462000173270ustar00/* $NetBSD: cd9660_conversion.c,v 1.4 2007/03/14 14:11:17 christos Exp $ */ /* * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan * Perez-Rathke and Ram Vedam. All rights reserved. * * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, * Alan Perez-Rathke and Ram Vedam. * * 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 DANIEL WATT, WALTER DEIGNAN, RYAN * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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. */ #define CD9660_CONVERSION_IMPL #include "cd9660.h" #include #if defined(__RCSID) && !defined(__lint) __RCSID("$NetBSD: cd9660_conversion.c,v 1.4 2007/03/14 14:11:17 christos Exp $"); __IDSTRING(mbsdid, "$MirOS: src/usr.sbin/makefs/cd9660/cd9660_conversion.c,v 1.8 2010/03/06 22:38:48 tg Exp $"); #endif /* !__lint */ static char cd9660_compute_gm_offset(time_t); #if 0 static inline int cd9660_pad_even(length) int length; { return length + (length & 0x01); } #endif /* * These can probably be implemented using a macro */ /* Little endian */ void cd9660_721(uint16_t w, unsigned char *twochar) { #if BYTE_ORDER == BIG_ENDIAN w = bswap16(w); #endif memcpy(twochar,&w,2); } void cd9660_731(uint32_t w, unsigned char *fourchar) { #if BYTE_ORDER == BIG_ENDIAN w = bswap32(w); #endif memcpy(fourchar,&w,4); } /* Big endian */ void cd9660_722(uint16_t w, unsigned char *twochar) { #if BYTE_ORDER == LITTLE_ENDIAN w = bswap16(w); #endif memcpy(twochar,&w,2); } void cd9660_732(uint32_t w, unsigned char *fourchar) { #if BYTE_ORDER == LITTLE_ENDIAN w = bswap32(w); #endif memcpy(fourchar,&w,4); } /** * Convert a dword into a double endian string of eight characters * @param int The double word to convert * @param char* The string to write the both endian double word to - It is assumed this is allocated and at least * eight characters long */ void cd9660_bothendian_dword(uint32_t dw, unsigned char *eightchar) { uint32_t le, be; #if BYTE_ORDER == LITTLE_ENDIAN le = dw; be = bswap32(dw); #endif #if BYTE_ORDER == BIG_ENDIAN be = dw; le = bswap32(dw); #endif memcpy(eightchar, &le, 4); memcpy((eightchar+4), &be, 4); } /** * Convert a word into a double endian string of four characters * @param int The word to convert * @param char* The string to write the both endian word to - It is assumed this is allocated and at least * four characters long */ void cd9660_bothendian_word(uint16_t dw, unsigned char *fourchar) { uint16_t le, be; #if BYTE_ORDER == LITTLE_ENDIAN le = dw; be = bswap16(dw); #endif #if BYTE_ORDER == BIG_ENDIAN be = dw; le = bswap16(dw); #endif memcpy(fourchar, &le, 2); memcpy((fourchar+2), &be, 2); } void cd9660_pad_string_spaces_(char *str, size_t len) { size_t i; for (i = 0; i < len; i ++) { if (str[i] == '\0') str[i] = 0x20; } } static char cd9660_compute_gm_offset(time_t tim) { struct tm t, gm; (void)localtime_r(&tim, &t); (void)gmtime_r(&tim, &gm); gm.tm_year -= t.tm_year; gm.tm_yday -= t.tm_yday; gm.tm_hour -= t.tm_hour; gm.tm_min -= t.tm_min; if (gm.tm_year < 0) gm.tm_yday = -1; else if (gm.tm_year > 0) gm.tm_yday = 1; return (char)(-(gm.tm_min + 60* (24 * gm.tm_yday + gm.tm_hour)) / 15); } /* Long dates: 17 characters */ void cd9660_time_8426(time_t tim, unsigned char *buf) { struct tm t; char temp[18]; (void)localtime_r(&tim, &t); (void)snprintf(temp, sizeof(temp), "%04i%02i%02i%02i%02i%02i%02i", 1900+(int)t.tm_year, (int)t.tm_mon+1, (int)t.tm_mday, (int)t.tm_hour, (int)t.tm_min, (int)t.tm_sec, 0); (void)memcpy(buf, temp, 16); buf[16] = cd9660_compute_gm_offset(tim); } /* Short dates: 7 characters */ void cd9660_time_915(time_t tim, unsigned char *buf) { struct tm t; (void)localtime_r(&tim, &t); buf[0] = t.tm_year; buf[1] = t.tm_mon+1; buf[2] = t.tm_mday; buf[3] = t.tm_hour; buf[4] = t.tm_min; buf[5] = t.tm_sec; buf[6] = cd9660_compute_gm_offset(tim); } int cd9660_isthisa_time_8426_utc(const char *val, const char *fieldtitle) { int i, j; if (val == NULL || strlen(val) != 16) { warnx("error: The %s requires a 16 bytes 8.4.26 time argument", fieldtitle); return (0); } j = 0; for (i = 0; i < 16; ++i) if (val[i] < '0' || val[i] > '9') { warnx("error: The %s must be composed of digits", fieldtitle); return (0); } else if (val[i] != '0') j = 1; if (j == 0) /* 16 times '0' */ return (1); #define otoa(x) (val[x] - '0') if (val[0] == '0' && val[1] == '0' && val[2] == '0' && val[3] == '0') { warnx("error: The %s %s is invalid (%s)", fieldtitle, "year", "too small"); return (0); } i = otoa(4) * 10 + otoa(5); if (i < 1 || i > 12) { warnx("error: The %s %s is invalid (%s)", fieldtitle, "month", "not 1..12"); return (0); } i = otoa(6) * 10 + otoa(7); if (i < 1 || i > 31) { warnx("error: The %s %s is invalid (%s)", fieldtitle, "day", "not 1..31"); return (0); } i = otoa(8) * 10 + otoa(9); if (i > 23) { warnx("error: The %s %s is invalid (%s)", fieldtitle, "hour", "not 0..23"); return (0); } i = otoa(10) * 10 + otoa(11); if (i > 59) { warnx("error: The %s %s is invalid (%s)", fieldtitle, "minute", "not 0..59"); return (0); } i = otoa(12) * 10 + otoa(13); if (i > 59) { /* but in ECMA 119: should be 0..60 */ warnx("error: The %s %s is invalid (%s)", fieldtitle, "second", "not 0..59"); return (0); } #undef otoa /* hundredths are 00..99 alright */ /* offset is NUL (UTC) alright */ return (1); } makefs/src/usr.sbin/makefs/cd9660/cd9660_debug.c010064400000000000000000000333041134456207000162200ustar00/* $NetBSD: cd9660_debug.c,v 1.9 2009/01/08 22:28:45 bjh21 Exp $ */ /* * Copyright (c) 2009 * Thorsten Glaser * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan * Perez-Rathke and Ram Vedam. All rights reserved. * * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, * Alan Perez-Rathke and Ram Vedam. * * 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 DANIEL WATT, WALTER DEIGNAN, RYAN * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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 HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #include #include #if defined(__RCSID) && !defined(__lint) __IDSTRING(mbsdid, "$MirOS: src/usr.sbin/makefs/cd9660/cd9660_debug.c,v 1.4 2010/03/06 23:24:16 tg Exp $"); __RCSID("$NetBSD: cd9660_debug.c,v 1.9 2009/01/08 22:28:45 bjh21 Exp $"); #endif /* !__lint */ #if !HAVE_NBTOOL_CONFIG_H #include #endif #include "makefs.h" #include "cd9660.h" #include "iso9660_rrip.h" static void debug_print_susp_attrs(cd9660node *, int); static void debug_dump_to_xml_padded_hex_output(const char *, unsigned char *, int); static inline void print_n_tabs(int n) { int i; for (i = 1; i <= n; i ++) printf("\t"); } #if 0 void debug_print_rrip_info(n) cd9660node *n; { struct ISO_SUSP_ATTRIBUTES *t; TAILQ_FOREACH(t, &node->head, rr_ll) { } } #endif static void debug_print_susp_attrs(cd9660node *n, int indent) { struct ISO_SUSP_ATTRIBUTES *t; TAILQ_FOREACH(t, &n->head, rr_ll) { print_n_tabs(indent); printf("-"); printf("%c%c: L:%i",t->attr.su_entry.SP.h.type[0], t->attr.su_entry.SP.h.type[1], (int)t->attr.su_entry.SP.h.length[0]); printf("\n"); } } void debug_print_tree(cd9660node *node, int level) { #if !HAVE_NBTOOL_CONFIG_H cd9660node *cn; print_n_tabs(level); if (node->type & CD9660_TYPE_DOT) { printf(". (%i)\n", isonum_733(node->isoDirRecord->extent)); } else if (node->type & CD9660_TYPE_DOTDOT) { printf("..(%i)\n", isonum_733(node->isoDirRecord->extent)); } else if (node->isoDirRecord->name[0]=='\0') { printf("(ROOT) (%i to %i)\n", node->fileDataSector, node->fileDataSector + node->fileSectorsUsed - 1); } else { printf("%s (%s) (%i to %i)\n", node->isoDirRecord->name, (node->isoDirRecord->flags[0] & ISO_FLAG_DIRECTORY) ? "DIR" : "FILE", node->fileDataSector, (node->fileSectorsUsed == 0) ? node->fileDataSector : node->fileDataSector + node->fileSectorsUsed - 1); } if (diskStructure.rock_ridge_enabled) debug_print_susp_attrs(node, level + 1); TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) debug_print_tree(cn, level + 1); #else printf("Sorry, debugging is not supported in host-tools mode.\n"); #endif } void debug_print_path_tree(cd9660node *n) { cd9660node *iterator = n; /* Only display this message when called with the root node */ if (n->parent == NULL) printf("debug_print_path_table: Dumping path table contents\n"); while (iterator != NULL) { if (iterator->isoDirRecord->name[0] == '\0') printf("0) (ROOT)\n"); else printf("%i) %s\n", iterator->level, iterator->isoDirRecord->name); iterator = iterator->ptnext; } } void debug_print_volume_descriptor_information(void) { volume_descriptor *tmp = diskStructure.firstVolumeDescriptor; char temp[2048]; printf("==Listing Volume Descriptors==\n"); while (tmp != NULL) { memset(temp, 0, 2048); memcpy(temp, tmp->volumeDescriptorData + 1, 5); printf("Volume descriptor in sector %i: type %i, ID %s\n", tmp->sector, tmp->volumeDescriptorData[0], temp); switch(tmp->volumeDescriptorData[0]) { case 0:/*boot record*/ break; case 1: /* PVD */ break; case 2: /* SVD */ break; case 3: /* Volume Partition Descriptor */ break; case 255: /* terminator */ break; } tmp = tmp->next; } printf("==Done Listing Volume Descriptors==\n"); } void debug_dump_to_xml_ptentry(path_table_entry *pttemp, int num, int mode) { printf("\n" ,num); printf("%i\n", pttemp->length[0]); printf("%i\n", pttemp->extended_attribute_length[0]); printf("%i\n", debug_get_encoded_number(pttemp->parent_number,mode)); debug_dump_to_xml_padded_hex_output("name", pttemp->name, pttemp->length[0]); printf("\n"); } void debug_dump_to_xml_path_table(FILE *fd, int sector, int size, int mode) { path_table_entry pttemp; int t = 0; int n = 0; fseek(fd, 2048 * sector, SEEK_SET); while (t < size) { /* Read fixed data first */ fread(&pttemp, 1, 8, fd); t += 8; /* Read variable */ fread(((unsigned char*)&pttemp) + 8, 1, pttemp.length[0], fd); t += pttemp.length[0]; debug_dump_to_xml_ptentry(&pttemp, n, mode); n++; } } /* * XML Debug output functions * Dump hierarchy of CD, as well as volume info, to XML * Can be used later to diff against a standard, * or just provide easy to read detailed debug output */ void debug_dump_to_xml(FILE *fd) { unsigned char buf[2048]; int sector; int t, t2; struct iso_primary_descriptor primaryVD; struct _boot_volume_descriptor bootVD; printf("\n"); /* Display Volume Descriptors */ sector = 16; do { fseek(fd, 2048*sector, SEEK_SET); fread(buf, 1, 2048, fd); t = (int)((unsigned char)buf[0]); switch (t) { case 0: memcpy(&bootVD, buf, 2048); break; case 1: memcpy(&primaryVD, buf, 2048); break; } debug_dump_to_xml_volume_descriptor(buf, sector); sector++; } while (t != 255); t = debug_get_encoded_number((u_char *)primaryVD.type_l_path_table, 731); t2 = debug_get_encoded_number((u_char *)primaryVD.path_table_size, 733); printf("Path table 1 located at sector %i and is %i bytes long\n", t,t2); debug_dump_to_xml_path_table(fd, t, t2, 721); t = debug_get_encoded_number((u_char *)primaryVD.type_m_path_table, 731); debug_dump_to_xml_path_table(fd, t, t2, 722); printf("\n"); } static void debug_dump_to_xml_padded_hex_output(const char *element, unsigned char *buf, int len) { int i; int t; printf("<%s>",element); for (i = 0; i < len; i++) { t = (unsigned char)buf[i]; if (t >= 32 && t < 127) printf("%c",t); } printf("\n",element); printf("<%s:hex>",element); for (i = 0; i < len; i++) { t = (unsigned char)buf[i]; printf(" %x",t); } printf("\n",element); } int debug_get_encoded_number(unsigned char* buf, int mode) { #if !HAVE_NBTOOL_CONFIG_H switch (mode) { /* 711: Single bite */ case 711: return isonum_711(buf); /* 712: Single signed byte */ case 712: return isonum_712((char *)buf); /* 721: 16 bit LE */ case 721: return isonum_721(buf); /* 731: 32 bit LE */ case 731: return isonum_731(buf); /* 722: 16 bit BE */ case 722: return isonum_722(buf); /* 732: 32 bit BE */ case 732: return isonum_732(buf); /* 723: 16 bit bothE */ case 723: return isonum_723(buf); /* 733: 32 bit bothE */ case 733: return isonum_733(buf); } #endif return 0; } void debug_dump_integer(const char *element, char* buf, int mode) { printf("<%s>%i\n", element, debug_get_encoded_number((unsigned char *)buf, mode), element); } void debug_dump_string(const char *element __unused, unsigned char *buf __unused, int len __unused) { } void debug_dump_directory_record_9_1(unsigned char* buf) { printf("\n"); debug_dump_integer("length", ((struct iso_directory_record*) buf)->length, 711); debug_dump_integer("ext_attr_length", ((struct iso_directory_record*) buf)->ext_attr_length,711); debug_dump_integer("extent", (char *)((struct iso_directory_record*) buf)->extent, 733); debug_dump_integer("size", (char *)((struct iso_directory_record*) buf)->size, 733); debug_dump_integer("flags", ((struct iso_directory_record*) buf)->flags, 711); debug_dump_integer("file_unit_size", ((struct iso_directory_record*) buf)->file_unit_size,711); debug_dump_integer("interleave", ((struct iso_directory_record*) buf)->interleave, 711); debug_dump_integer("volume_sequence_number", ((struct iso_directory_record*) buf)->volume_sequence_number, 723); debug_dump_integer("name_len", ((struct iso_directory_record*) buf)->name_len, 711); debug_dump_to_xml_padded_hex_output("name", (u_char *)((struct iso_directory_record*) buf)->name, debug_get_encoded_number((u_char *) ((struct iso_directory_record*) buf)->length, 711)); printf("\n"); } void debug_dump_to_xml_volume_descriptor(unsigned char* buf, int sector) { printf("\n", sector); printf(""); switch(buf[0]) { case 0: printf("boot"); break; case 1: printf("primary"); break; case 2: printf("supplementary"); break; case 3: printf("volume partition descriptor"); break; case 255: printf("terminator"); break; } printf("\n"); switch(buf[0]) { case 1: debug_dump_integer("type", ((struct iso_primary_descriptor*)buf)->type, 711); debug_dump_to_xml_padded_hex_output("id", (u_char *)((struct iso_primary_descriptor*) buf)->id, ISODCL ( 2, 6)); debug_dump_integer("version", ((struct iso_primary_descriptor*)buf)->version, 711); debug_dump_to_xml_padded_hex_output("system_id", (u_char *)((struct iso_primary_descriptor*)buf)->system_id, ISODCL(9,40)); debug_dump_to_xml_padded_hex_output("volume_id", (u_char *)((struct iso_primary_descriptor*)buf)->volume_id, ISODCL(41,72)); debug_dump_integer("volume_space_size", ((struct iso_primary_descriptor*)buf)->volume_space_size, 733); debug_dump_integer("volume_set_size", ((struct iso_primary_descriptor*)buf)->volume_set_size, 733); debug_dump_integer("volume_sequence_number", ((struct iso_primary_descriptor*)buf)->volume_sequence_number, 723); debug_dump_integer("logical_block_size", ((struct iso_primary_descriptor*)buf)->logical_block_size, 723); debug_dump_integer("path_table_size", ((struct iso_primary_descriptor*)buf)->path_table_size, 733); debug_dump_integer("type_l_path_table", ((struct iso_primary_descriptor*)buf)->type_l_path_table, 731); debug_dump_integer("opt_type_l_path_table", ((struct iso_primary_descriptor*)buf)->opt_type_l_path_table, 731); debug_dump_integer("type_m_path_table", ((struct iso_primary_descriptor*)buf)->type_m_path_table, 732); debug_dump_integer("opt_type_m_path_table", ((struct iso_primary_descriptor*)buf)->opt_type_m_path_table,732); debug_dump_directory_record_9_1( (u_char *)((struct iso_primary_descriptor*)buf)->root_directory_record); debug_dump_to_xml_padded_hex_output("volume_set_id", (u_char *)((struct iso_primary_descriptor*) buf)->volume_set_id, ISODCL (191, 318)); debug_dump_to_xml_padded_hex_output("publisher_id", (u_char *)((struct iso_primary_descriptor*) buf)->publisher_id, ISODCL (319, 446)); debug_dump_to_xml_padded_hex_output("preparer_id", (u_char *)((struct iso_primary_descriptor*) buf)->preparer_id, ISODCL (447, 574)); debug_dump_to_xml_padded_hex_output("application_id", (u_char *)((struct iso_primary_descriptor*) buf)->application_id, ISODCL (575, 702)); debug_dump_to_xml_padded_hex_output("copyright_file_id", (u_char *)((struct iso_primary_descriptor*) buf)->copyright_file_id, ISODCL (703, 739)); debug_dump_to_xml_padded_hex_output("abstract_file_id", (u_char *)((struct iso_primary_descriptor*) buf)->abstract_file_id, ISODCL (740, 776)); debug_dump_to_xml_padded_hex_output("bibliographic_file_id", (u_char *)((struct iso_primary_descriptor*) buf)->bibliographic_file_id, ISODCL (777, 813)); debug_dump_to_xml_padded_hex_output("creation_date", (u_char *)((struct iso_primary_descriptor*) buf)->creation_date, ISODCL (814, 830)); debug_dump_to_xml_padded_hex_output("modification_date", (u_char *)((struct iso_primary_descriptor*) buf)->modification_date, ISODCL (831, 847)); debug_dump_to_xml_padded_hex_output("expiration_date", (u_char *)((struct iso_primary_descriptor*) buf)->expiration_date, ISODCL (848, 864)); debug_dump_to_xml_padded_hex_output("effective_date", (u_char *)((struct iso_primary_descriptor*) buf)->effective_date, ISODCL (865, 881)); debug_dump_to_xml_padded_hex_output("file_structure_version", (u_char *)((struct iso_primary_descriptor*) buf)->file_structure_version, ISODCL(882,882)); break; } printf("\n"); } makefs/src/usr.sbin/makefs/cd9660/cd9660_eltorito.c010064400000000000000000000406621134457064600170100ustar00/* $NetBSD: cd9660_eltorito.c,v 1.12 2008/07/27 10:29:32 reinoud Exp $ */ /* * Copyright (c) 2010 * Thorsten Glaser * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan * Perez-Rathke and Ram Vedam. All rights reserved. * * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, * Alan Perez-Rathke and Ram Vedam. * * 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 DANIEL WATT, WALTER DEIGNAN, RYAN * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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 "cd9660.h" #include "cd9660_eltorito.h" #include #if defined(__RCSID) && !defined(__lint) __RCSID("$NetBSD: cd9660_eltorito.c,v 1.12 2008/07/27 10:29:32 reinoud Exp $"); __IDSTRING(mbsdid, "$MirOS: src/usr.sbin/makefs/cd9660/cd9660_eltorito.c,v 1.10 2010/03/07 00:21:34 tg Exp $"); #endif /* !__lint */ #ifdef DEBUG #define ELTORITO_DPRINTF(__x) printf __x #else #define ELTORITO_DPRINTF(__x) #endif #define cd9660_init_boot_catalog_entry() \ ((struct boot_catalog_entry *)(calloc(1, sizeof (struct boot_catalog_entry)))) static struct boot_catalog_entry *cd9660_boot_setup_validation_entry(char); static struct boot_catalog_entry *cd9660_boot_setup_default_entry( struct cd9660_boot_image *); static struct boot_catalog_entry *cd9660_boot_setup_section_head(char); #if 0 static u_char cd9660_boot_get_system_type(struct cd9660_boot_image *); #endif int cd9660_add_boot_disk(const char *boot_info) { struct stat stbuf; const char *mode_msg; char *temp; char *sysname; char *filename; struct cd9660_boot_image *new_image, *tmp_image; assert(boot_info != NULL); if (*boot_info == '\0') { warnx("Error: Boot disk information must be in the " "format 'system;filename'"); return 0; } /* First decode the boot information */ if ((temp = strdup(boot_info)) == NULL) { warn("%s: strdup", __func__); return 0; } sysname = temp; filename = strchr(sysname, ';'); if (filename == NULL) { warnx("supply boot disk information in the format " "'system;filename'"); free(temp); return 0; } *filename++ = '\0'; if (diskStructure.verbose_level > 0) { printf("Found bootdisk with system %s, and filename %s\n", sysname, filename); } if ((new_image = calloc(1, sizeof (*new_image))) == NULL) { warn("%s: malloc", __func__); free(temp); return 0; } new_image->loadSegment = 0; /* default for now */ /* Decode System */ if (strcmp(sysname, "i386") == 0) new_image->system = ET_SYS_X86; else if (strcmp(sysname, "powerpc") == 0) new_image->system = ET_SYS_PPC; else if (strcmp(sysname, "macppc") == 0 || strcmp(sysname, "mac68k") == 0) new_image->system = ET_SYS_MAC; else { warnx("boot disk system must be " "i386, powerpc, macppc, or mac68k"); free(temp); free(new_image); return 0; } if ((new_image->filename = strdup(filename)) == NULL) { warn("%s: strdup", __func__); free(temp); free(new_image); return 0; } free(temp); /* Get information about the file */ if (lstat(new_image->filename, &stbuf) == -1) err(EXIT_FAILURE, "%s: lstat(\"%s\")", __func__, new_image->filename); switch (stbuf.st_size) { case 1440 * 1024: new_image->targetMode = ET_MEDIA_144FDD; mode_msg = "Assigned boot image to 1.44 emulation mode"; break; case 1200 * 1024: new_image->targetMode = ET_MEDIA_12FDD; mode_msg = "Assigned boot image to 1.2 emulation mode"; break; case 2880 * 1024: new_image->targetMode = ET_MEDIA_288FDD; mode_msg = "Assigned boot image to 2.88 emulation mode"; break; default: new_image->targetMode = ET_MEDIA_NOEM; mode_msg = "Assigned boot image to no emulation mode"; break; } if (diskStructure.verbose_level > 0) printf("%s\n", mode_msg); new_image->size = stbuf.st_size; new_image->num_sectors = howmany(new_image->size, diskStructure.sectorSize) * howmany(diskStructure.sectorSize, 512); if (diskStructure.verbose_level > 0) { printf("New image has size %d, uses %d 512-byte sectors\n", new_image->size, new_image->num_sectors); } new_image->sector = -1; /* Bootable by default */ new_image->bootable = ET_BOOTABLE; /* Add boot disk */ /* Group images for the same platform together. */ TAILQ_FOREACH(tmp_image, &diskStructure.boot_images, image_list) { if (tmp_image->system != new_image->system) break; } if (tmp_image == NULL) { TAILQ_INSERT_HEAD(&diskStructure.boot_images, new_image, image_list); } else TAILQ_INSERT_BEFORE(tmp_image, new_image, image_list); new_image->serialno = diskStructure.image_serialno++; /* TODO : Need to do anything about the boot image in the tree? */ diskStructure.is_bootable = 1; return 1; } int cd9660_eltorito_add_boot_option(const char *option_string, const char *value) { char *eptr; struct cd9660_boot_image *image; assert(option_string != NULL); /* Find the last image added */ TAILQ_FOREACH(image, &diskStructure.boot_images, image_list) { if (image->serialno + 1 == diskStructure.image_serialno) break; } if (image == NULL) errx(EXIT_FAILURE, "Attempted to add boot option, " "but no boot images have been specified"); if (strcmp(option_string, "no-emul-boot") == 0) { image->targetMode = ET_MEDIA_NOEM; } else if (strcmp(option_string, "no-boot") == 0) { image->bootable = ET_NOT_BOOTABLE; } else if (strcmp(option_string, "hard-disk-boot") == 0) { image->targetMode = ET_MEDIA_HDD; } else if (strcmp(option_string, "boot-load-segment") == 0) { image->loadSegment = strtoul(value, &eptr, 16); if (eptr == value || *eptr != '\0' || errno != ERANGE) { warn("%s: strtoul", __func__); return 0; } } else if (strcmp(option_string, "boot-info-table") == 0) { image->infoTable = 1; } else { return 0; } return 1; } static struct boot_catalog_entry * cd9660_boot_setup_validation_entry(char sys) { struct boot_catalog_entry *entry; boot_catalog_validation_entry *ve; int16_t checksum; unsigned char *csptr; unsigned int i; entry = cd9660_init_boot_catalog_entry(); if (entry == NULL) { warnx("Error: memory allocation failed in " "cd9660_boot_setup_validation_entry"); return 0; } ve = &entry->entry_data.VE; ve->header_id[0] = 1; ve->platform_id[0] = sys; ve->key[0] = 0x55; ve->key[1] = 0xAA; /* Calculate checksum */ checksum = 0; cd9660_721(0, ve->checksum); csptr = (unsigned char*)ve; for (i = 0; i < sizeof(*ve); i += 2) { checksum += (int16_t)csptr[i]; checksum += 256 * (int16_t)csptr[i + 1]; } checksum = -checksum; cd9660_721(checksum, ve->checksum); ELTORITO_DPRINTF(("%s: header_id %d, platform_id %d, key[0] %d, key[1] %d, " "checksum %04x\n", __func__, ve->header_id[0], ve->platform_id[0], ve->key[0], ve->key[1], checksum)); return entry; } static struct boot_catalog_entry * cd9660_boot_setup_default_entry(struct cd9660_boot_image *disk) { struct boot_catalog_entry *default_entry; boot_catalog_initial_entry *ie; default_entry = cd9660_init_boot_catalog_entry(); if (default_entry == NULL) return NULL; ie = &default_entry->entry_data.IE; ie->boot_indicator[0] = disk->bootable; ie->media_type[0] = disk->targetMode; cd9660_721(disk->loadSegment, ie->load_segment); ie->system_type[0] = disk->system; cd9660_721(disk->num_sectors, ie->sector_count); cd9660_731(disk->sector, ie->load_rba); ELTORITO_DPRINTF(("%s: boot indicator %d, media type %d, " "load segment %04x, system type %d, sector count %d, " "load rba %d\n", __func__, ie->boot_indicator[0], ie->media_type[0], disk->loadSegment, ie->system_type[0], disk->num_sectors, disk->sector)); return default_entry; } static struct boot_catalog_entry * cd9660_boot_setup_section_head(char platform) { struct boot_catalog_entry *entry; boot_catalog_section_header *sh; entry = cd9660_init_boot_catalog_entry(); if (entry == NULL) return NULL; sh = &entry->entry_data.SH; /* More by default. The last one will manually be set to 0x91 */ sh->header_indicator[0] = ET_SECTION_HEADER_MORE; sh->platform_id[0] = platform; sh->num_section_entries[0] = 0; return entry; } static struct boot_catalog_entry * cd9660_boot_setup_section_entry(struct cd9660_boot_image *disk) { struct boot_catalog_entry *entry; boot_catalog_section_entry *se; if ((entry = cd9660_init_boot_catalog_entry()) == NULL) return NULL; se = &entry->entry_data.SE; se->boot_indicator[0] = ET_BOOTABLE; se->media_type[0] = disk->targetMode; cd9660_721(disk->loadSegment, se->load_segment); cd9660_721(disk->num_sectors, se->sector_count); cd9660_731(disk->sector, se->load_rba); return entry; } #if 0 static u_char cd9660_boot_get_system_type(struct cd9660_boot_image *disk) { /* For hard drive booting, we need to examine the MBR to figure out what the partition type is */ return 0; } #endif /* * Set up the BVD, Boot catalog, and the boot entries, but do no writing */ int cd9660_setup_boot(int first_sector) { int sector; int used_sectors; int num_entries = 0; int catalog_sectors; struct boot_catalog_entry *x86_head, *mac_head, *ppc_head, *valid_entry, *default_entry, *temp, *head, **headp, *next; struct cd9660_boot_image *tmp_disk; headp = NULL; x86_head = mac_head = ppc_head = NULL; /* If there are no boot disks, don't bother building boot information */ if (TAILQ_EMPTY(&diskStructure.boot_images)) return 0; /* Point to catalog: For now assume it consumes one sector */ ELTORITO_DPRINTF(("Boot catalog will go in sector %d\n", first_sector)); diskStructure.boot_catalog_sector = first_sector; cd9660_731(first_sector, diskStructure.boot_descriptor->boot_catalog_pointer); /* Step 1: Generate boot catalog */ /* Step 1a: Validation entry */ valid_entry = cd9660_boot_setup_validation_entry(ET_SYS_X86); if (valid_entry == NULL) return -1; /* * Count how many boot images there are, * and how many sectors they consume. */ num_entries = 1; used_sectors = 0; TAILQ_FOREACH(tmp_disk, &diskStructure.boot_images, image_list) { used_sectors += tmp_disk->num_sectors; /* One default entry per image */ num_entries++; } catalog_sectors = howmany(num_entries * 0x20, diskStructure.sectorSize); used_sectors += catalog_sectors; if (diskStructure.verbose_level > 0) { printf("%s: there will be %i entries consuming %i sectors. " "Catalog is %i sectors\n", __func__, num_entries, used_sectors, catalog_sectors); } /* Populate sector numbers */ sector = first_sector + catalog_sectors; TAILQ_FOREACH(tmp_disk, &diskStructure.boot_images, image_list) { tmp_disk->sector = sector; sector += tmp_disk->num_sectors; } LIST_INSERT_HEAD(&diskStructure.boot_entries, valid_entry, ll_struct); /* Step 1b: Initial/default entry */ /* TODO : PARAM */ tmp_disk = TAILQ_FIRST(&diskStructure.boot_images); default_entry = cd9660_boot_setup_default_entry(tmp_disk); if (default_entry == NULL) { warnx("Error: memory allocation failed in cd9660_setup_boot"); return -1; } LIST_INSERT_AFTER(valid_entry, default_entry, ll_struct); /* Todo: multiple default entries? */ tmp_disk = TAILQ_NEXT(tmp_disk, image_list); temp = default_entry; /* If multiple boot images are given : */ while (tmp_disk != NULL) { /* Step 2: Section header */ switch (tmp_disk->system) { case ET_SYS_X86: headp = &x86_head; break; case ET_SYS_PPC: headp = &ppc_head; break; case ET_SYS_MAC: headp = &mac_head; break; default: warnx("%s: internal error: unknown system type", __func__); return -1; } if (*headp == NULL) { head = cd9660_boot_setup_section_head(tmp_disk->system); if (head == NULL) { warnx("Error: memory allocation failed in " "cd9660_setup_boot"); return -1; } LIST_INSERT_AFTER(default_entry, head, ll_struct); *headp = head; } else head = *headp; head->entry_data.SH.num_section_entries[0]++; /* Step 2a: Section entry and extensions */ temp = cd9660_boot_setup_section_entry(tmp_disk); if (temp == NULL) { warn("%s: cd9660_boot_setup_section_entry", __func__); return -1; } while ((next = LIST_NEXT(head, ll_struct)) != NULL && next->entry_type == ET_ENTRY_SE) head = next; LIST_INSERT_AFTER(head, temp, ll_struct); tmp_disk = TAILQ_NEXT(tmp_disk, image_list); } /* TODO: Remaining boot disks when implemented */ return first_sector + used_sectors; } int cd9660_setup_boot_volume_descriptor(volume_descriptor *bvd) { boot_volume_descriptor *bvdData = (boot_volume_descriptor*)bvd->volumeDescriptorData; bvdData->boot_record_indicator[0] = ISO_VOLUME_DESCRIPTOR_BOOT; memcpy(bvdData->identifier, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5); bvdData->version[0] = 1; memcpy(bvdData->boot_system_identifier, ET_ID, 23); memcpy(bvdData->identifier, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5); diskStructure.boot_descriptor = (boot_volume_descriptor*) bvd->volumeDescriptorData; return 1; } int cd9660_write_boot(FILE *fd) { struct boot_catalog_entry *e; struct cd9660_boot_image *t; long oofs, fofs; /* write boot catalog */ fseek(fd, diskStructure.boot_catalog_sector * diskStructure.sectorSize, SEEK_SET); if (diskStructure.verbose_level > 0) { printf("Writing boot catalog to sector %d\n", diskStructure.boot_catalog_sector); } LIST_FOREACH(e, &diskStructure.boot_entries, ll_struct) { if (diskStructure.verbose_level > 0) { printf("Writing catalog entry of type %d\n", e->entry_type); } /* * It doesnt matter which one gets written * since they are the same size */ fwrite(&(e->entry_data.VE), 1, 32, fd); } if (diskStructure.verbose_level > 0) printf("Finished writing boot catalog\n"); /* copy boot images */ TAILQ_FOREACH(t, &diskStructure.boot_images, image_list) { if (diskStructure.verbose_level > 0) { printf("Writing boot image from %s to sectors %d\n", t->filename, t->sector); } cd9660_copy_file(fd, t->sector, t->filename); if (t->infoTable) { if (diskStructure.verbose_level > 0) printf("Writing boot info table into image... "); oofs = ftell(fd); fseek(fd, t->sector * diskStructure.sectorSize, SEEK_SET); fofs = ftell(fd); if (oofs - fofs < 64) printf("failed: boot image too small\n"); else { uint32_t cksum_val = 0; long cofs = fofs + 64; unsigned char cksum_buf[4]; unsigned char bitable[56]; volume_descriptor *vd; memset(bitable, 0, sizeof(bitable)); fseek(fd, cofs, SEEK_SET); while (cofs < oofs) { if (fread(cksum_buf, 1, 4, fd) != 4 || ferror(fd)) break; cofs += 4; cksum_val += cksum_buf[0] | (cksum_buf[1] << 8) | (cksum_buf[2] << 16) | (cksum_buf[3] << 24); } vd = diskStructure.firstVolumeDescriptor; while (vd->volumeDescriptorData[0] != ISO_VOLUME_DESCRIPTOR_PVD) if (vd->volumeDescriptorData[0] == ISO_VOLUME_DESCRIPTOR_TERMINATOR) break; else if (vd->next) vd = vd->next; else break; cd9660_731(vd->sector, bitable + 8 - 8); cd9660_731(t->sector, bitable + 12 - 8); cd9660_731(oofs - fofs, bitable + 16 - 8); cd9660_731(cksum_val, bitable + 20 - 8); fseek(fd, fofs + 8, SEEK_SET); fwrite(bitable, 1, 56, fd); if (ferror(fd)) printf("failed: error during write\n"); else printf("ok: PVD %u, boot %u@%u, cksum %08X\n", (uint32_t)vd->sector, (uint32_t)(oofs - fofs), (uint32_t)t->sector, cksum_val); } fseek(fd, oofs, SEEK_SET); } } return 0; } makefs/src/usr.sbin/makefs/cd9660/cd9660_eltorito.h010064400000000000000000000124571134456303000170030ustar00/* $NetBSD: cd9660_eltorito.h,v 1.5 2009/07/04 14:31:38 ahoka Exp $ */ /* * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan * Perez-Rathke and Ram Vedam. All rights reserved. * * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, * Alan Perez-Rathke and Ram Vedam. * * 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 DANIEL WATT, WALTER DEIGNAN, RYAN * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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 _CD9660_ELTORITO_H_ #define _CD9660_ELTORITO_H_ /* Boot defines */ #define ET_ID "EL TORITO SPECIFICATION" #define ET_SYS_X86 0 #define ET_SYS_PPC 1 #define ET_SYS_MAC 2 #define ET_BOOT_ENTRY_SIZE 0x20 #define ET_BOOTABLE 0x88 #define ET_NOT_BOOTABLE 0 #define ET_MEDIA_NOEM 0 #define ET_MEDIA_12FDD 1 #define ET_MEDIA_144FDD 2 #define ET_MEDIA_288FDD 3 #define ET_MEDIA_HDD 4 #define ET_INDICATOR_HEADERMORE 0x90 #define ET_INDICATOR_HEADERLAST 0x91 #define ET_INDICATOR_EXTENSION 0x44 /*** Boot Structures ***/ typedef struct _boot_volume_descriptor { u_char boot_record_indicator [ISODCL(0x00,0x00)]; u_char identifier [ISODCL(0x01,0x05)]; u_char version [ISODCL(0x06,0x06)]; u_char boot_system_identifier [ISODCL(0x07,0x26)]; u_char unused1 [ISODCL(0x27,0x46)]; u_char boot_catalog_pointer [ISODCL(0x47,0x4A)]; u_char unused2 [ISODCL(0x4B,0x7FF)]; } boot_volume_descriptor; typedef struct _boot_catalog_validation_entry { u_char header_id [ISODCL(0x00,0x00)]; u_char platform_id [ISODCL(0x01,0x01)]; u_char reserved1 [ISODCL(0x02,0x03)]; u_char manufacturer [ISODCL(0x04,0x1B)]; u_char checksum [ISODCL(0x1C,0x1D)]; u_char key [ISODCL(0x1E,0x1F)]; } boot_catalog_validation_entry; typedef struct _boot_catalog_initial_entry { u_char boot_indicator [ISODCL(0x00,0x00)]; u_char media_type [ISODCL(0x01,0x01)]; u_char load_segment [ISODCL(0x02,0x03)]; u_char system_type [ISODCL(0x04,0x04)]; u_char unused_1 [ISODCL(0x05,0x05)]; u_char sector_count [ISODCL(0x06,0x07)]; u_char load_rba [ISODCL(0x08,0x0B)]; u_char unused_2 [ISODCL(0x0C,0x1F)]; } boot_catalog_initial_entry; #define ET_SECTION_HEADER_MORE 0x90 #define ET_SECTION_HEADER_LAST 0x91 typedef struct _boot_catalog_section_header { u_char header_indicator [ISODCL(0x00,0x00)]; u_char platform_id [ISODCL(0x01,0x01)]; u_char num_section_entries [ISODCL(0x02,0x03)]; u_char id_string [ISODCL(0x04,0x1F)]; } boot_catalog_section_header; typedef struct _boot_catalog_section_entry { u_char boot_indicator [ISODCL(0x00,0x00)]; u_char media_type [ISODCL(0x01,0x01)]; u_char load_segment [ISODCL(0x02,0x03)]; u_char system_type [ISODCL(0x04,0x04)]; u_char unused_1 [ISODCL(0x05,0x05)]; u_char sector_count [ISODCL(0x06,0x07)]; u_char load_rba [ISODCL(0x08,0x0B)]; u_char selection_criteria [ISODCL(0x0C,0x0C)]; u_char vendor_criteria [ISODCL(0x0D,0x1F)]; } boot_catalog_section_entry; typedef struct _boot_catalog_section_entry_extension { u_char extension_indicator [ISODCL(0x00,0x00)]; u_char flags [ISODCL(0x01,0x01)]; u_char vendor_criteria [ISODCL(0x02,0x1F)]; } boot_catalog_section_entry_extension; #define ET_ENTRY_VE 1 #define ET_ENTRY_IE 2 #define ET_ENTRY_SH 3 #define ET_ENTRY_SE 4 #define ET_ENTRY_EX 5 struct boot_catalog_entry { char entry_type; union { boot_catalog_validation_entry VE; boot_catalog_initial_entry IE; boot_catalog_section_header SH; boot_catalog_section_entry SE; boot_catalog_section_entry_extension EX; } entry_data; LIST_ENTRY(boot_catalog_entry) ll_struct; }; /* Temporary structure */ struct cd9660_boot_image { char *filename; int size; int sector; /* copied to LoadRBA */ int num_sectors; unsigned int loadSegment; u_char targetMode; u_char system; u_char bootable; u_char infoTable; /* * If the boot image exists in the filesystem * already, this is a pointer to that node. For the sake * of simplicity in future versions, this pointer is only * to the node in the primary volume. This SHOULD be done * via a hashtable lookup. */ struct _cd9660node *boot_image_node; TAILQ_ENTRY(cd9660_boot_image) image_list; int serialno; }; #endif /* _CD9660_ELTORITO_H_ */ makefs/src/usr.sbin/makefs/cd9660/cd9660_strings.c010064400000000000000000000070341134454447300166330ustar00/* $NetBSD: cd9660_strings.c,v 1.4 2007/01/16 17:32:05 hubertf Exp $ */ /* * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan * Perez-Rathke and Ram Vedam. All rights reserved. * * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, * Alan Perez-Rathke and Ram Vedam. * * 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 DANIEL WATT, WALTER DEIGNAN, RYAN * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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 HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #else #ifdef __MirBSD__ #include #endif #include #endif #include #include #include #include "makefs.h" #include "cd9660.h" #if defined(__RCSID) && !defined(__lint) __RCSID("$NetBSD: cd9660_strings.c,v 1.4 2007/01/16 17:32:05 hubertf Exp $"); __IDSTRING(mbsdid, "$MirOS: src/usr.sbin/makefs/cd9660/cd9660_strings.c,v 1.3 2010/03/06 21:29:07 tg Exp $"); #endif /* !__lint */ void cd9660_uppercase_characters(char *str, int len) { int p; for (p = 0; p < len; p++) { if (islower((unsigned char)str[p]) ) str[p] -= 32; } } static inline int cd9660_is_a_char(char c) { return (isupper((unsigned char)c) || c == '_' || (c >= '0' && c <= '?')); } static inline int cd9660_is_d_char(char c) { return (isupper((unsigned char)c) || c == '_' || (c >= '%' && c <= '9') || (c >= ' ' && c <= '\"')); } /* * Test a string to see if it is composed of valid a characters * @param const char* The string to test * @returns int 1 if valid, 2 if valid if characters are converted to * upper case, 0 otherwise */ int cd9660_valid_a_chars(const char *str) { const char *c = str; int upperFound = 0; while ((*c) != '\0') { if (!(cd9660_is_a_char(*c))) { if (islower((unsigned char)*c) ) upperFound = 1; else return 0; } c++; } return upperFound + 1; } /* * Test a string to see if it is composed of valid d characters * @param const char* The string to test * @returns int 1 if valid, 2 if valid if characters are converted to * upper case, 0 otherwise */ int cd9660_valid_d_chars(const char *str) { const char *c=str; int upperFound = 0; while ((*c) != '\0') { if (!(cd9660_is_d_char(*c))) { if (islower((unsigned char)*c) ) upperFound = 1; else return 0; } c++; } return upperFound + 1; } makefs/src/usr.sbin/makefs/cd9660/cd9660_write.c010064400000000000000000000343701134456207000162700ustar00/* $NetBSD: cd9660_write.c,v 1.12 2009/11/22 18:43:27 mbalmer Exp $ */ /* * Copyright (c) 2009 * Thorsten Glaser * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan * Perez-Rathke and Ram Vedam. All rights reserved. * * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, * Alan Perez-Rathke and Ram Vedam. * * 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 DANIEL WATT, WALTER DEIGNAN, RYAN * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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 "cd9660.h" #include "iso9660_rrip.h" #include #if defined(__RCSID) && !defined(__lint) __IDSTRING(mbsdid, "$MirOS: src/usr.sbin/makefs/cd9660/cd9660_write.c,v 1.11 2010/03/06 23:24:16 tg Exp $"); __RCSID("$NetBSD: cd9660_write.c,v 1.12 2009/11/22 18:43:27 mbalmer Exp $"); #endif /* !__lint */ #include static int cd9660_write_volume_descriptors(FILE *); static int cd9660_write_path_table(FILE *, int, int); static int cd9660_write_path_tables(FILE *); static int cd9660_write_file(FILE *, cd9660node *); static int cd9660_write_filedata(FILE *, int, const unsigned char *, int); #if 0 static int cd9660_write_buffered(FILE *, int, int, const unsigned char*); #endif static void cd9660_write_rr(FILE *, cd9660node *, int, int); /* * Write the image * Writes the entire image * @param const char* The filename for the image * @returns int 1 on success, 0 on failure */ int cd9660_write_image(const char* image) { FILE *fd; int status; char buf[2048]; if ((fd = fopen(image, "w+")) == NULL) { err(EXIT_FAILURE, "%s: Can't open `%s' for writing", __func__, image); } if (diskStructure.verbose_level > 0) printf("Writing image\n"); /* write padding first, it gets overwritten if we have none */ memset(buf, 0, 2048); cd9660_write_filedata(fd, diskStructure.totalSectors - 1, (unsigned char *)buf, 1); if (diskStructure.has_generic_bootimage) { status = cd9660_copy_file(fd, 0, diskStructure.generic_bootimage); if (status == 0) { warnx("%s: Error writing generic boot image", __func__); goto cleanup_bad_image; } } /* Write the volume descriptors */ status = cd9660_write_volume_descriptors(fd); if (status == 0) { warnx("%s: Error writing volume descriptors to image", __func__); goto cleanup_bad_image; } if (diskStructure.verbose_level > 0) printf("Volume descriptors written\n"); /* * Write the path tables: there are actually four, but right * now we are only concearned with two. */ status = cd9660_write_path_tables(fd); if (status == 0) { warnx("%s: Error writing path tables to image", __func__); goto cleanup_bad_image; } if (diskStructure.verbose_level > 0) printf("Path tables written\n"); /* Write the directories and files */ status = cd9660_write_file(fd, diskStructure.rootNode); if (status == 0) { warnx("%s: Error writing files to image", __func__); goto cleanup_bad_image; } if (diskStructure.is_bootable) { cd9660_write_boot(fd); } if (diskStructure.verbose_level > 0) printf("Files written\n"); fclose(fd); if (diskStructure.verbose_level > 0) printf("Image closed\n"); return 1; cleanup_bad_image: fclose(fd); if (!diskStructure.keep_bad_images) unlink(image); if (diskStructure.verbose_level > 0) printf("Bad image cleaned up\n"); return 0; } static int cd9660_write_volume_descriptors(FILE *fd) { volume_descriptor *vd_temp = diskStructure.firstVolumeDescriptor; int pos; while (vd_temp != NULL) { pos = vd_temp->sector*diskStructure.sectorSize; cd9660_write_filedata(fd, vd_temp->sector, vd_temp->volumeDescriptorData, 1); vd_temp = vd_temp->next; } return 1; } /* * Write out an individual path table * Used just to keep redundant code to a minimum * @param FILE *fd Valid file pointer * @param int Sector to start writing path table to * @param int Endian mode : BIG_ENDIAN or LITTLE_ENDIAN * @returns int 1 on success, 0 on failure */ static int cd9660_write_path_table(FILE *fd, int sector, int mode) { int path_table_sectors = CD9660_BLOCKS(diskStructure.sectorSize, diskStructure.pathTableLength); unsigned char *buffer; unsigned char *buffer_head; int len; path_table_entry temp_entry; cd9660node *ptcur; buffer = calloc(diskStructure.sectorSize, path_table_sectors); if (buffer == NULL) { warnx("%s: Memory allocation error allocating buffer", __func__); return 0; } buffer_head = buffer; ptcur = diskStructure.rootNode; while (ptcur != NULL) { memset(&temp_entry, 0, sizeof(path_table_entry)); temp_entry.length[0] = ptcur->isoDirRecord->name_len[0]; temp_entry.extended_attribute_length[0] = ptcur->isoDirRecord->ext_attr_length[0]; memcpy(temp_entry.name, ptcur->isoDirRecord->name, temp_entry.length[0] + 1); /* round up */ len = temp_entry.length[0] + 8 + (temp_entry.length[0] & 0x01); /* todo: function pointers instead */ if (mode == LITTLE_ENDIAN) { cd9660_731(ptcur->fileDataSector, temp_entry.first_sector); cd9660_721((ptcur->parent == NULL ? 1 : ptcur->parent->ptnumber), temp_entry.parent_number); } else { cd9660_732(ptcur->fileDataSector, temp_entry.first_sector); cd9660_722((ptcur->parent == NULL ? 1 : ptcur->parent->ptnumber), temp_entry.parent_number); } memcpy(buffer, &temp_entry, len); buffer += len; ptcur = ptcur->ptnext; } return cd9660_write_filedata(fd, sector, buffer_head, path_table_sectors); } /* * Write out the path tables to disk * Each file descriptor should be pointed to by the PVD, so we know which * sector to copy them to. One thing to watch out for: the only path tables * stored are in the endian mode that the application is compiled for. So, * the first thing to do is write out that path table, then to write the one * in the other endian mode requires to convert the endianness of each entry * in the table. The best way to do this would be to create a temporary * path_table_entry structure, then for each path table entry, copy it to * the temporary entry, translate, then copy that to disk. * * @param FILE* Valid file descriptor * @returns int 0 on failure, 1 on success */ static int cd9660_write_path_tables(FILE *fd) { if (cd9660_write_path_table(fd, diskStructure.primaryLittleEndianTableSector, LITTLE_ENDIAN) == 0) return 0; if (cd9660_write_path_table(fd, diskStructure.primaryBigEndianTableSector, BIG_ENDIAN) == 0) return 0; /* @TODO: handle remaining two path tables */ return 1; } /* * Write a file to disk * Writes a file, its directory record, and its data to disk * This file is designed to be called RECURSIVELY, so initially call it * with the root node. All of the records should store what sector the * file goes in, so no computation should be necessary. * * @param int fd Valid file descriptor * @param struct cd9660node* writenode Pointer to the file to be written * @returns int 0 on failure, 1 on success */ static int cd9660_write_file(FILE *fd, cd9660node *writenode) { char *buf; char *temp_file_name; int ret; int working_sector; int cur_sector_offset; int written; iso_directory_record_cd9660 temp_record; cd9660node *temp; int rv = 0; /* Todo : clean up variables */ temp_file_name = calloc(1, CD9660MAXPATH + 1); if (temp_file_name == NULL) err(EXIT_FAILURE, "%s: malloc", __func__); buf = malloc(diskStructure.sectorSize); if (buf == NULL) err(EXIT_FAILURE, "%s: malloc", __func__); if ((writenode->level != 0) && !(writenode->node->type & S_IFDIR)) { fsinode *inode = writenode->node->inode; /* Only attempt to write unwritten files that have length. */ if ((inode->flags & FI_WRITTEN) != 0) { INODE_WARNX(("%s: skipping written inode %d", __func__, (int)inode->st.st_ino)); } else if (writenode->fileDataLength > 0) { INODE_WARNX(("%s: writing inode %d blocks at %" PRIu32, __func__, (int)inode->st.st_ino, inode->ino)); inode->flags |= FI_WRITTEN; cd9660_compute_full_filename(writenode, temp_file_name, CD9660MAXPATH + 1, 0); ret = cd9660_copy_file(fd, writenode->fileDataSector, temp_file_name); if (ret == 0) goto out; } } else { /* * Here is a new revelation that ECMA didnt explain * (at least not well). * ALL . and .. records store the name "\0" and "\1" * resepctively. So, for each directory, we have to * make a new node. * * This is where it gets kinda messy, since we have to * be careful of sector boundaries */ cur_sector_offset = 0; working_sector = writenode->fileDataSector; fseek(fd, working_sector * diskStructure.sectorSize, SEEK_SET); /* * Now loop over children, writing out their directory * records - beware of sector boundaries */ TAILQ_FOREACH(temp, &writenode->cn_children, cn_next_child) { /* * Copy the temporary record and adjust its size * if necessary */ memcpy(&temp_record, temp->isoDirRecord, sizeof(iso_directory_record_cd9660)); temp_record.length[0] = cd9660_compute_record_size(temp); if (temp_record.length[0] + cur_sector_offset >= diskStructure.sectorSize) { cur_sector_offset = 0; working_sector++; /* Seek to the next sector. */ fseek(fd, working_sector * diskStructure.sectorSize, SEEK_SET); } /* Write out the basic ISO directory record */ written = fwrite(&temp_record, 1, temp->isoDirRecord->length[0], fd); if (diskStructure.rock_ridge_enabled) { cd9660_write_rr(fd, temp, cur_sector_offset, working_sector); } fseek(fd, working_sector * diskStructure.sectorSize + cur_sector_offset + temp_record.length[0] - temp->su_tail_size, SEEK_SET); if (temp->su_tail_size > 0) fwrite(temp->su_tail_data, 1, temp->su_tail_size, fd); if (ferror(fd)) { warnx("%s: write error", __func__); goto out; } cur_sector_offset += temp_record.length[0]; } /* * Recurse on children. */ TAILQ_FOREACH(temp, &writenode->cn_children, cn_next_child) { if ((ret = cd9660_write_file(fd, temp)) == 0) goto out; } } rv = 1; out: free(temp_file_name); free(buf); return rv; } /* * Wrapper function to write a buffer (one sector) to disk. * Seeks and writes the buffer. * NOTE: You dont NEED to use this function, but it might make your * life easier if you have to write things that align to a sector * (such as volume descriptors). * * @param int fd Valid file descriptor * @param int sector Sector number to write to * @param const unsigned char* Buffer to write. This should be the * size of a sector, and if only a portion * is written, the rest should be set to 0. */ static int cd9660_write_filedata(FILE *fd, int sector, const unsigned char *buf, int numsecs) { off_t curpos; size_t success; curpos = ftello(fd); fseek(fd, sector * diskStructure.sectorSize, SEEK_SET); success = fwrite(buf, diskStructure.sectorSize * numsecs, 1, fd); fseek(fd, curpos, SEEK_SET); if (success == 1) success = diskStructure.sectorSize * numsecs; return success; } #if 0 static int cd9660_write_buffered(FILE *fd, int offset, int buff_len, const unsigned char* buffer) { static int working_sector = -1; static char buf[2048]; return 0; } #endif int cd9660_copy_file(FILE *fd, int start_sector, const char *filename) { FILE *rf; int bytes_read; int sector = start_sector; int buf_size = diskStructure.sectorSize; char *buf; buf = malloc(buf_size); if (buf == NULL) err(EXIT_FAILURE, "%s: malloc", __func__); if ((rf = fopen(filename, "rb")) == NULL) { warn("%s: cannot open %s", __func__, filename); free(buf); return 0; } if (diskStructure.verbose_level > 1) printf("Writing file: %s\n",filename); fseek(fd, start_sector * diskStructure.sectorSize, SEEK_SET); while (!feof(rf)) { bytes_read = fread(buf,1,buf_size,rf); if (ferror(rf)) { warn("%s: fread", __func__); free(buf); return 0; } fwrite(buf,1,bytes_read,fd); if (ferror(fd)) { warn("%s: fwrite", __func__); free(buf); return 0; } sector++; } fclose(rf); free(buf); return 1; } static void cd9660_write_rr(FILE *fd, cd9660node *writenode, int offset, int sector) { int in_ca = 0; struct ISO_SUSP_ATTRIBUTES *myattr; offset += writenode->isoDirRecord->length[0]; fseek(fd, sector * diskStructure.sectorSize + offset, SEEK_SET); /* Offset now points at the end of the record */ TAILQ_FOREACH(myattr, &writenode->head, rr_ll) { fwrite(&(myattr->attr), CD9660_SUSP_ENTRY_SIZE(myattr), 1, fd); if (!in_ca) { offset += CD9660_SUSP_ENTRY_SIZE(myattr); if (myattr->last_in_suf) { /* * Point the offset to the start of this * record's CE area */ fseek(fd, (diskStructure. susp_continuation_area_start_sector * diskStructure.sectorSize) + writenode->susp_entry_ce_start, SEEK_SET); in_ca = 1; } } } if (!in_ca && (offset & 1)) { /* align to even size */ char x = 0; fwrite(&x, 1, 1, fd); } /* * If we had to go to the continuation area, head back to * where we should be. */ if (in_ca) fseek(fd, sector * diskStructure.sectorSize + offset, SEEK_SET); } makefs/src/usr.sbin/makefs/cd9660/iso9660_rrip.c010064400000000000000000000606411134454447300163250ustar00/* $NetBSD: iso9660_rrip.c,v 1.8 2009/01/10 22:06:29 bjh21 Exp $ */ /* * Copyright (c) 2009, 2010 * Thorsten Glaser * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan * Perez-Rathke and Ram Vedam. All rights reserved. * * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, * Alan Perez-Rathke and Ram Vedam. * * 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 DANIEL WATT, WALTER DEIGNAN, RYAN * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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. */ /* This will hold all the function definitions * defined in iso9660_rrip.h */ #include "makefs.h" #include "cd9660.h" #include "iso9660_rrip.h" #include #include #include #if defined(__RCSID) && !defined(__lint) __IDSTRING(mbsdid, "$MirOS: src/usr.sbin/makefs/cd9660/iso9660_rrip.c,v 1.22 2010/03/06 21:29:07 tg Exp $"); __RCSID("$NetBSD: iso9660_rrip.c,v 1.8 2009/01/10 22:06:29 bjh21 Exp $"); #endif /* !__lint */ static void cd9660_rrip_initialize_inode(cd9660node *); static int cd9660_susp_handle_continuation(cd9660node *); static int cd9660_susp_handle_continuation_common(cd9660node *, int); int cd9660_susp_initialize(cd9660node *node, cd9660node *parent, cd9660node *grandparent, int shortcut) { cd9660node *cn; int r; /* Make sure the node is not NULL. If it is, there are major problems */ assert(node != NULL); if (shortcut == 2) { shortcut = 0; goto cd9660_susp_initialize_shortcut; } if (!(node->type & CD9660_TYPE_DOT) && !(node->type & CD9660_TYPE_DOTDOT)) TAILQ_INIT(&(node->head)); if (node->dot_record != 0) TAILQ_INIT(&(node->dot_record->head)); if (node->dot_dot_record != 0) TAILQ_INIT(&(node->dot_dot_record->head)); /* SUSP specific entries here */ if ((r = cd9660_susp_initialize_node(node)) < 0) return r; /* currently called cd9660node_rrip_init_links */ r = cd9660_rrip_initialize_node(node, parent, grandparent); if (r < 0) return r; /* * If we are the root node, we need to initialise the entries for * our dot node now (out of the below loop) with shortcut set to * 1, then move all of its entries (save PX to prevent duplicates) * into it, then finalise the dot node out of bounds but after us * again. The “shortcut” variable is unused after the label, thus * it can be used as temporary to speed up the loop. */ if (shortcut == 1) return 1; cd9660_susp_initialize_shortcut: /* assert: shortcut == 0 here */ if (node == diskStructure.rootNode) { /* We are the root node. */ struct ISO_SUSP_ATTRIBUTES *temp; /* Initialise our dot record. */ if ((r = cd9660_susp_initialize(node->dot_record, node, parent, 1)) < 0) return r; /* Move SUSP entries. */ while ((temp = TAILQ_FIRST(&node->head))) { TAILQ_REMOVE(&node->head, temp, rr_ll); if (temp->attr.su_entry.SP.h.type[0] != 'P' || temp->attr.su_entry.SP.h.type[1] != 'X') TAILQ_INSERT_TAIL(&node->dot_record->head, temp, rr_ll); } /* Note that we are the root node. */ shortcut = 1; /* Now go on process ourselves normally. */ } /* * See if we need a CE record, and set all of the * associated counters. * * This should be called after all extensions. After * this is called, no new records should be added. */ if ((r = cd9660_susp_handle_continuation(node)) < 0) return r; /* Recurse on children. */ TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) { if ((r = cd9660_susp_initialize(cn, node, parent, /* root node? */ shortcut && cn == node->dot_record ? /* finish only */ 2 : /* full operation */ 0)) < 0) return 0; } return 1; } int cd9660_susp_finalize(cd9660node *node) { cd9660node *temp; int r; assert(node != NULL); if (node == diskStructure.rootNode) diskStructure.susp_continuation_area_current_free = 0; if ((r = cd9660_susp_finalize_node(node)) < 0) return r; if ((r = cd9660_rrip_finalize_node(node)) < 0) return r; TAILQ_FOREACH(temp, &node->cn_children, cn_next_child) { if ((r = cd9660_susp_finalize(temp)) < 0) return r; } return 1; } /* * If we really wanted to speed things up, we could have some sort of * lookup table on the SUSP entry type that calls a functor. Or, we could * combine the functions. These functions are kept separate to allow * easier addition of other extensions. * For the sake of simplicity and clarity, we won't be doing that for now. */ /* * SUSP needs to update the following types: * CE (continuation area) */ int cd9660_susp_finalize_node(cd9660node *node) { struct ISO_SUSP_ATTRIBUTES *t; /* Handle CE counters */ if (node->susp_entry_ce_length > 0) { node->susp_entry_ce_start = diskStructure.susp_continuation_area_current_free; diskStructure.susp_continuation_area_current_free += node->susp_entry_ce_length; } TAILQ_FOREACH(t, &node->head, rr_ll) { if (t->susp_type != SUSP_TYPE_SUSP || t->entry_type != SUSP_ENTRY_SUSP_CE) continue; cd9660_bothendian_dword( diskStructure. susp_continuation_area_start_sector, t->attr.su_entry.CE.ca_sector); cd9660_bothendian_dword( diskStructure. susp_continuation_area_start_sector, t->attr.su_entry.CE.ca_sector); cd9660_bothendian_dword(node->susp_entry_ce_start, t->attr.su_entry.CE.offset); cd9660_bothendian_dword(node->susp_entry_ce_length, t->attr.su_entry.CE.length); } return 0; } int cd9660_rrip_finalize_node(cd9660node *node) { struct ISO_SUSP_ATTRIBUTES *t; TAILQ_FOREACH(t, &node->head, rr_ll) { if (t->susp_type != SUSP_TYPE_RRIP) continue; switch (t->entry_type) { case SUSP_ENTRY_RRIP_CL: /* Look at rr_relocated*/ if (node->rr_relocated == NULL) return -1; cd9660_bothendian_dword( node->rr_relocated->fileDataSector, t->attr.rr_entry.CL.dir_loc); break; case SUSP_ENTRY_RRIP_PL: /* Look at rr_real_parent */ if (node->parent->rr_real_parent == NULL) return -1; cd9660_bothendian_dword( node->parent->rr_real_parent->fileDataSector, t->attr.rr_entry.PL.dir_loc); break; } } return 0; } static int cd9660_susp_handle_continuation_common(cd9660node *node, int space) { int ca_used, susp_used, susp_used_pre_ce, working; struct ISO_SUSP_ATTRIBUTES *temp, *pre_ce, *last, *CE, *ST; pre_ce = last = NULL; working = 254 - space; if (node->su_tail_size > 0) /* Allow 4 bytes for "ST" record. */ working -= node->su_tail_size + 4; #ifdef DEBUG printf("There are %i bytes to work with\n", working); #endif susp_used_pre_ce = susp_used = 0; ca_used = 0; TAILQ_FOREACH(temp, &node->head, rr_ll) { if (working < 0) break; #ifdef DEBUG printf("SUSP entry %c%c found, length %d\n", temp->attr.su_entry.SP.h.type[0], temp->attr.su_entry.SP.h.type[1], temp->attr.su_entry.SP.h.length[0]); #endif working -= CD9660_SUSP_ENTRY_SIZE(temp); if (working >= 0) { last = temp; susp_used += CD9660_SUSP_ENTRY_SIZE(temp); } if (working >= 28) { /* * Remember the last entry after which we * could insert a "CE" entry. */ pre_ce = last; susp_used_pre_ce = susp_used; } } /* A CE entry is needed */ if (working <= 0) { CE = cd9660node_susp_create_node(SUSP_TYPE_SUSP, SUSP_ENTRY_SUSP_CE, "CE", SUSP_LOC_ENTRY); cd9660_susp_ce(CE, node); /* This will automatically insert at the appropriate location */ if (pre_ce != NULL) TAILQ_INSERT_AFTER(&node->head, pre_ce, CE, rr_ll); else TAILQ_INSERT_HEAD(&node->head, CE, rr_ll); last = CE; susp_used = susp_used_pre_ce + 28; /* Count how much CA data is necessary */ for (temp = TAILQ_NEXT(last, rr_ll); temp != NULL; temp = TAILQ_NEXT(temp, rr_ll)) { ca_used += CD9660_SUSP_ENTRY_SIZE(temp); } } /* An ST entry is needed */ if (node->su_tail_size > 0) { ST = cd9660node_susp_create_node(SUSP_TYPE_SUSP, SUSP_ENTRY_SUSP_ST, "ST", SUSP_LOC_ENTRY); cd9660_susp_st(ST, node); if (last != NULL) TAILQ_INSERT_AFTER(&node->head, last, ST, rr_ll); else TAILQ_INSERT_HEAD(&node->head, ST, rr_ll); last = ST; susp_used += 4; } if (last != NULL) last->last_in_suf = 1; /* align to even size */ node->susp_entry_size = ((susp_used + 1) >> 1) << 1; node->susp_entry_ce_length = ca_used; diskStructure.susp_continuation_area_size += ca_used; return 1; } /* See if a continuation entry is needed for each of the different types */ static int cd9660_susp_handle_continuation(cd9660node *node) { assert (node != NULL); /* Entry */ if (cd9660_susp_handle_continuation_common( node,(int)(node->isoDirRecord->length[0])) < 0) return 0; return 1; } int cd9660_susp_initialize_node(cd9660node *node) { struct ISO_SUSP_ATTRIBUTES *temp; /* * Requirements/notes: * CE: is added for us where needed * ST: not sure if it is even required, but if so, should be * handled by the CE code * PD: isnt needed (though might be added for testing) * SP: is stored ONLY on the . record of the root directory * ES: not sure */ /* Check for root directory, add SP and ER if needed. */ if (node->type & CD9660_TYPE_DOT) { if (node->parent == diskStructure.rootNode) { temp = cd9660node_susp_create_node(SUSP_TYPE_SUSP, SUSP_ENTRY_SUSP_SP, "SP", SUSP_LOC_DOT); cd9660_susp_sp(temp, node); /* Should be first entry. */ TAILQ_INSERT_HEAD(&node->head, temp, rr_ll); } } return 1; } static void cd9660_rrip_initialize_inode(cd9660node *node) { struct ISO_SUSP_ATTRIBUTES *attr; /* * Inode dependent values - this may change, * but for now virtual files and directories do * not have an inode structure */ if ((node->node != NULL) && (node->node->inode != NULL)) { /* PX - POSIX attributes */ attr = cd9660node_susp_create_node(SUSP_TYPE_RRIP, SUSP_ENTRY_RRIP_PX, "PX", SUSP_LOC_ENTRY); cd9660node_rrip_px(attr, node->node); TAILQ_INSERT_TAIL(&node->head, attr, rr_ll); /* TF - timestamp */ attr = cd9660node_susp_create_node(SUSP_TYPE_RRIP, SUSP_ENTRY_RRIP_TF, "TF", SUSP_LOC_ENTRY); cd9660node_rrip_tf(attr, node->node); TAILQ_INSERT_TAIL(&node->head, attr, rr_ll); /* SL - Symbolic link */ /* ?????????? Dan - why is this here? */ if (TAILQ_EMPTY(&node->cn_children) && node->node->inode != NULL && S_ISLNK(node->node->inode->st.st_mode)) cd9660_createSL(node); /* PN - device number */ if (node->node->inode != NULL && ((S_ISCHR(node->node->inode->st.st_mode) || S_ISBLK(node->node->inode->st.st_mode)))) { attr = cd9660node_susp_create_node(SUSP_TYPE_RRIP, SUSP_ENTRY_RRIP_PN, "PN", SUSP_LOC_ENTRY); cd9660node_rrip_pn(attr, node->node); TAILQ_INSERT_TAIL(&node->head, attr, rr_ll); } } } int cd9660_rrip_initialize_node(cd9660node *node, cd9660node *parent, cd9660node *grandparent) { struct ISO_SUSP_ATTRIBUTES *current = NULL; assert(node != NULL); if (node->type & CD9660_TYPE_DOT) { /* * Handle ER - should be the only entry to appear on * a "." record */ if (node->parent == diskStructure.rootNode) { cd9660_susp_ER(node, 1, SUSP_RRIP_ER_EXT_ID, SUSP_RRIP_ER_EXT_DES, SUSP_RRIP_ER_EXT_SRC); } if (parent != NULL && parent->node != NULL && parent->node->inode != NULL) { /* PX - POSIX attributes */ current = cd9660node_susp_create_node(SUSP_TYPE_RRIP, SUSP_ENTRY_RRIP_PX, "PX", SUSP_LOC_ENTRY); cd9660node_rrip_px(current, parent->node); TAILQ_INSERT_TAIL(&node->head, current, rr_ll); } } else if (node->type & CD9660_TYPE_DOTDOT) { if (grandparent != NULL && grandparent->node != NULL && grandparent->node->inode != NULL) { /* PX - POSIX attributes */ current = cd9660node_susp_create_node(SUSP_TYPE_RRIP, SUSP_ENTRY_RRIP_PX, "PX", SUSP_LOC_ENTRY); cd9660node_rrip_px(current, grandparent->node); TAILQ_INSERT_TAIL(&node->head, current, rr_ll); } } else { cd9660_rrip_initialize_inode(node); /* * Not every node needs a NM set - only if the name is * actually different. IE: If a file is TEST -> TEST, * no NM. test -> TEST, need a NM * * The rr_moved_dir needs to be assigned a NM record as well. */ if (node == diskStructure.rr_moved_dir) { cd9660_rrip_add_NM(node, RRIP_DEFAULT_MOVE_DIR_NAME); } else if ((node->node != NULL) && ((strlen(node->node->name) != (int)node->isoDirRecord->name_len[0]) || (memcmp(node->node->name,node->isoDirRecord->name, (int) node->isoDirRecord->name_len[0]) != 0))) { cd9660_rrip_NM(node); } /* Rock ridge directory relocation code here. */ /* First handle the CL for the placeholder file. */ if (node->rr_relocated != NULL) { current = cd9660node_susp_create_node(SUSP_TYPE_RRIP, SUSP_ENTRY_RRIP_CL, "CL", SUSP_LOC_ENTRY); cd9660_rrip_CL(current, node); TAILQ_INSERT_TAIL(&node->head, current, rr_ll); } /* Handle RE*/ if (node->rr_real_parent != NULL) { current = cd9660node_susp_create_node(SUSP_TYPE_RRIP, SUSP_ENTRY_RRIP_RE, "RE", SUSP_LOC_ENTRY); cd9660_rrip_RE(current,node); TAILQ_INSERT_TAIL(&node->head, current, rr_ll); /* Handle PL */ current = cd9660node_susp_create_node(SUSP_TYPE_RRIP, SUSP_ENTRY_RRIP_PL, "PL", SUSP_LOC_DOTDOT); cd9660_rrip_PL(current,node->dot_dot_record); TAILQ_INSERT_TAIL(&node->dot_dot_record->head, current, rr_ll); } } return 1; } struct ISO_SUSP_ATTRIBUTES* cd9660node_susp_create_node(int susp_type, int entry_type, const char *type_id, int write_loc) { struct ISO_SUSP_ATTRIBUTES* temp; if ((temp = calloc(1, sizeof (struct ISO_SUSP_ATTRIBUTES))) == NULL) { CD9660_MEM_ALLOC_ERROR("cd9660node_susp_create_node"); exit(1); } temp->susp_type = susp_type; temp->entry_type = entry_type; temp->last_in_suf = 0; /* Phase this out */ temp->type_of[0] = type_id[0]; temp->type_of[1] = type_id[1]; temp->write_location = write_loc; /* * Since the first four bytes is common, lets go ahead and * set the type identifier, since we are passing that to this * function anyhow. */ temp->attr.su_entry.SP.h.type[0] = type_id[0]; temp->attr.su_entry.SP.h.type[1] = type_id[1]; return temp; } int cd9660_rrip_PL(struct ISO_SUSP_ATTRIBUTES* p, cd9660node *node __unused) { p->attr.rr_entry.PL.h.length[0] = 12; p->attr.rr_entry.PL.h.version[0] = 1; return 1; } int cd9660_rrip_CL(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *node __unused) { p->attr.rr_entry.CL.h.length[0] = 12; p->attr.rr_entry.CL.h.version[0] = 1; return 1; } int cd9660_rrip_RE(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *node __unused) { p->attr.rr_entry.RE.h.length[0] = 0; p->attr.rr_entry.RE.h.version[0] = 1; return 1; } void cd9660_createSL(cd9660node *node) { struct ISO_SUSP_ATTRIBUTES* current; int path_count, dir_count, done, i, j, dir_copied; char temp_cr[255]; char temp_sl[255]; /* used in copying continuation entry*/ char* sl_ptr; sl_ptr = node->node->symlink; done = 0; path_count = 0; dir_count = 0; dir_copied = 0; current = cd9660node_susp_create_node(SUSP_TYPE_RRIP, SUSP_ENTRY_RRIP_SL, "SL", SUSP_LOC_ENTRY); current->attr.rr_entry.SL.h.version[0] = 1; current->attr.rr_entry.SL.flags[0] = SL_FLAGS_NONE; if (*sl_ptr == '/') { temp_cr[0] = SL_FLAGS_ROOT; temp_cr[1] = 0; memcpy(current->attr.rr_entry.SL.component + path_count, temp_cr, 2); path_count += 2; sl_ptr++; } for (i = 0; i < (dir_count + 2); i++) temp_cr[i] = '\0'; while (!done) { while ((*sl_ptr != '/') && (*sl_ptr != '\0')) { dir_copied = 1; if (*sl_ptr == '.') { if ((*(sl_ptr + 1) == '/') || (*(sl_ptr + 1) == '\0')) { temp_cr[0] = SL_FLAGS_CURRENT; sl_ptr++; } else if(*(sl_ptr + 1) == '.') { if ((*(sl_ptr + 2) == '/') || (*(sl_ptr + 2) == '\0')) { temp_cr[0] = SL_FLAGS_PARENT; sl_ptr += 2; } } else { temp_cr[dir_count+2] = *sl_ptr; sl_ptr++; dir_count++; } } else { temp_cr[dir_count + 2] = *sl_ptr; sl_ptr++; dir_count++; } } if ((path_count + dir_count) >= 249) { current->attr.rr_entry.SL.flags[0] |= SL_FLAGS_CONTINUE; j = 0; if (path_count <= 249) { while(j != (249 - path_count)) { temp_sl[j] = temp_cr[j]; j++; } temp_sl[0] = SL_FLAGS_CONTINUE; temp_sl[1] = j - 2; memcpy( current->attr.rr_entry.SL.component + path_count, temp_sl, j); } path_count += j; current->attr.rr_entry.SL.h.length[0] = path_count + 5; TAILQ_INSERT_TAIL(&node->head, current, rr_ll); current= cd9660node_susp_create_node(SUSP_TYPE_RRIP, SUSP_ENTRY_RRIP_SL, "SL", SUSP_LOC_ENTRY); current->attr.rr_entry.SL.h.version[0] = 1; current->attr.rr_entry.SL.flags[0] = SL_FLAGS_NONE; path_count = 0; if (dir_count > 2) { while (j != dir_count + 2) { current->attr.rr_entry.SL.component[ path_count + 2] = temp_cr[j]; j++; path_count++; } current->attr.rr_entry.SL.component[1] = path_count; path_count+= 2; } else { while(j != dir_count) { current->attr.rr_entry.SL.component[ path_count+2] = temp_cr[j]; j++; path_count++; } } } else { if (dir_copied == 1) { temp_cr[1] = dir_count; memcpy(current->attr.rr_entry.SL.component + path_count, temp_cr, dir_count + 2); path_count += dir_count + 2; } } if (*sl_ptr == '\0') { done = 1; current->attr.rr_entry.SL.h.length[0] = path_count + 5; TAILQ_INSERT_TAIL(&node->head, current, rr_ll); } else { sl_ptr++; dir_count = 0; dir_copied = 0; for(i = 0; i < 255; i++) { temp_cr[i] = '\0'; } } } } int cd9660node_rrip_px(struct ISO_SUSP_ATTRIBUTES *v, fsnode *pxinfo) { v->attr.rr_entry.PX.h.length[0] = 44; v->attr.rr_entry.PX.h.version[0] = 1; cd9660_bothendian_dword(rrip_squash ? /* turn on all R bits, and all X bits if one X bit is set */ (((pxinfo->inode->st.st_mode & 0111) ? 0555 : 0444) | /* preserve original mode bits, except W, setugid, sticky */ pxinfo->inode->st.st_mode) & ~07222 : pxinfo->inode->st.st_mode, v->attr.rr_entry.PX.mode); cd9660_bothendian_dword(pxinfo->inode->st.st_nlink, v->attr.rr_entry.PX.links); cd9660_bothendian_dword(rrip_squash ? 0 : pxinfo->inode->st.st_uid, v->attr.rr_entry.PX.uid); cd9660_bothendian_dword(rrip_squash ? 0 : pxinfo->inode->st.st_gid, v->attr.rr_entry.PX.gid); cd9660_bothendian_dword(pxinfo->inode->serno, v->attr.rr_entry.PX.serial); return 1; } int cd9660node_rrip_pn(struct ISO_SUSP_ATTRIBUTES *pn_field, fsnode *fnode) { pn_field->attr.rr_entry.PN.h.length[0] = 20; pn_field->attr.rr_entry.PN.h.version[0] = 1; if (sizeof (fnode->inode->st.st_rdev) > 32) cd9660_bothendian_dword((uint64_t)fnode->inode->st.st_rdev >> 32, pn_field->attr.rr_entry.PN.high); else cd9660_bothendian_dword(0, pn_field->attr.rr_entry.PN.high); cd9660_bothendian_dword(fnode->inode->st.st_rdev & 0xffffffff, pn_field->attr.rr_entry.PN.low); return 1; } #if 0 int cd9660node_rrip_nm(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *file_node) { int nm_length = strlen(file_node->isoDirRecord->name) + 5; p->attr.rr_entry.NM.h.type[0] = 'N'; p->attr.rr_entry.NM.h.type[1] = 'M'; snprintf(p->attr.rr_entry.NM.altname, sizeof (p->attr.rr_entry.NM.altname), "%s", file_node->isoDirRecord->name); p->attr.rr_entry.NM.h.length[0] = (unsigned char)nm_length; p->attr.rr_entry.NM.h.version[0] = (unsigned char)1; p->attr.rr_entry.NM.flags[0] = (unsigned char) NM_PARENT; return 1; } #endif int cd9660node_rrip_tf(struct ISO_SUSP_ATTRIBUTES *p, fsnode *_node) { struct cd9660node_rrip_tf_data { ISO_SUSP_HEADER h; u_char flags [ISODCL ( 4, 4)]; #if 0 u_char timestamp [ISODCL ( 5, 256)]; #else /* using short form */ u_char ts_mtime [ISODCL ( 5, 11)]; u_char ts_atime [ISODCL (12, 18)]; u_char ts_ctime [ISODCL (19, 25)]; u_char _ts_rest [ISODCL (26, 256)]; #endif } *tfp; /* memory alias */ tfp = (struct cd9660node_rrip_tf_data *)(&(p->attr.rr_entry.TF)); tfp->flags[0] = TF_MODIFY | TF_ACCESS | TF_ATTRIBUTES; tfp->h.length[0] = 4; tfp->h.version[0] = 1; /* * Need to add creation time, backup time, * expiration time, and effective time. */ cd9660_time_915(_node->inode->st.st_mtime, tfp->ts_mtime); tfp->h.length[0] += 7; cd9660_time_915(_node->inode->st.st_atime, tfp->ts_atime); tfp->h.length[0] += 7; cd9660_time_915(_node->inode->st.st_ctime, tfp->ts_ctime); tfp->h.length[0] += 7; return 1; } int cd9660_susp_sp(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *spinfo __unused) { p->attr.su_entry.SP.h.length[0] = 7; p->attr.su_entry.SP.h.version[0] = 1; p->attr.su_entry.SP.check[0] = 0xBE; p->attr.su_entry.SP.check[1] = 0xEF; p->attr.su_entry.SP.len_skp[0] = 0; return 1; } int cd9660_susp_st(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *stinfo __unused) { p->attr.su_entry.ST.h.type[0] = 'S'; p->attr.su_entry.ST.h.type[1] = 'T'; p->attr.su_entry.ST.h.length[0] = 4; p->attr.su_entry.ST.h.version[0] = 1; return 1; } int cd9660_susp_ce(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *spinfo __unused) { p->attr.su_entry.CE.h.length[0] = 28; p->attr.su_entry.CE.h.version[0] = 1; /* Other attributes dont matter right now, will be updated later */ return 1; } int cd9660_susp_pd(struct ISO_SUSP_ATTRIBUTES *p __unused, int length __unused) { return 1; } void cd9660_rrip_add_NM(cd9660node *node, const char *name) { int working,len; const char *p; struct ISO_SUSP_ATTRIBUTES *r; /* * Each NM record has 254 byes to work with. This means that * the name data itself only has 249 bytes to work with. So, a * name with 251 characters would require two nm records. */ p = name; working = 1; while (working) { r = cd9660node_susp_create_node(SUSP_TYPE_RRIP, SUSP_ENTRY_RRIP_NM, "NM", SUSP_LOC_ENTRY); r->attr.rr_entry.NM.h.version[0] = 1; r->attr.rr_entry.NM.flags[0] = RRIP_NM_FLAGS_NONE; len = strlen(p); if (len > 249) { len = 249; r->attr.rr_entry.NM.flags[0] = RRIP_NM_FLAGS_CONTINUE; } else { working = 0; } memcpy(r->attr.rr_entry.NM.altname, p, len); r->attr.rr_entry.NM.h.length[0] = 5 + len; TAILQ_INSERT_TAIL(&node->head, r, rr_ll); p += len; } } void cd9660_rrip_NM(cd9660node *node) { cd9660_rrip_add_NM(node, node->node->name); } struct ISO_SUSP_ATTRIBUTES* cd9660_susp_ER(cd9660node *node, u_char ext_version, const char* ext_id, const char* ext_des, const char* ext_src) { int l; struct ISO_SUSP_ATTRIBUTES *r; r = cd9660node_susp_create_node(SUSP_TYPE_SUSP, SUSP_ENTRY_SUSP_ER, "ER", SUSP_LOC_DOT); /* Fixed data is 8 bytes */ r->attr.su_entry.ER.h.length[0] = 8; r->attr.su_entry.ER.h.version[0] = 1; r->attr.su_entry.ER.len_id[0] = (u_char)strlen(ext_id); r->attr.su_entry.ER.len_des[0] = (u_char)strlen(ext_des); r->attr.su_entry.ER.len_src[0] = (u_char)strlen(ext_src); l = r->attr.su_entry.ER.len_id[0] + r->attr.su_entry.ER.len_src[0] + r->attr.su_entry.ER.len_des[0]; /* Everything must fit. */ assert(l + r->attr.su_entry.ER.h.length[0] <= 254); r->attr.su_entry.ER.h.length[0] += (u_char)l; r->attr.su_entry.ER.ext_ver[0] = ext_version; memcpy(r->attr.su_entry.ER.ext_data, ext_id, (int)r->attr.su_entry.ER.len_id[0]); l = (int) r->attr.su_entry.ER.len_id[0]; memcpy(r->attr.su_entry.ER.ext_data + l,ext_des, (int)r->attr.su_entry.ER.len_des[0]); l += (int)r->attr.su_entry.ER.len_des[0]; memcpy(r->attr.su_entry.ER.ext_data + l,ext_src, (int)r->attr.su_entry.ER.len_src[0]); TAILQ_INSERT_TAIL(&node->head, r, rr_ll); return r; } struct ISO_SUSP_ATTRIBUTES* cd9660_susp_ES(struct ISO_SUSP_ATTRIBUTES *last __unused, cd9660node *node __unused) { return NULL; } makefs/src/usr.sbin/makefs/cd9660/iso9660_rrip.h010064400000000000000000000207771134454447300163400ustar00/** $MirOS: src/usr.sbin/makefs/cd9660/iso9660_rrip.h,v 1.7 2010/03/06 21:29:07 tg Exp $ */ /* $NetBSD: iso9660_rrip.h,v 1.5 2009/01/10 22:06:29 bjh21 Exp $ */ /* * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan * Perez-Rathke and Ram Vedam. All rights reserved. * * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, * Alan Perez-Rathke and Ram Vedam. * * 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 DANIEL WATT, WALTER DEIGNAN, RYAN * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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 __ISO9660_RRIP_H__ #define __ISO9660_RRIP_H__ /* * This will hold all the functions needed to * write an ISO 9660 image with Rock Ridge Extensions */ /* For writing must use ISO_RRIP_EXTREF structure */ #include "makefs.h" #include #include "cd9660.h" #include #define PX_LENGTH 0x2C #define PN_LENGTH 0x14 #define TF_CREATION 0x01 #define TF_MODIFY 0x02 #define TF_ACCESS 0x04 #define TF_ATTRIBUTES 0x08 #define TF_BACKUP 0x10 #define TF_EXPIRATION 0x20 #define TF_EFFECTIVE 0x40 #define TF_LONGFORM 0x80 #define NM_CONTINUE 0x80 #define NM_CURRENT 0x100 #define NM_PARENT 0x200 #define SUSP_LOC_ENTRY 0x01 #define SUSP_LOC_DOT 0x02 #define SUSP_LOC_DOTDOT 0x04 #define SUSP_TYPE_SUSP 1 #define SUSP_TYPE_RRIP 2 #define SUSP_ENTRY_SUSP_CE 1 #define SUSP_ENTRY_SUSP_PD 2 #define SUSP_ENTRY_SUSP_SP 3 #define SUSP_ENTRY_SUSP_ST 4 #define SUSP_ENTRY_SUSP_ER 5 #define SUSP_ENTRY_SUSP_ES 6 #define SUSP_ENTRY_RRIP_PX 1 #define SUSP_ENTRY_RRIP_PN 2 #define SUSP_ENTRY_RRIP_SL 3 #define SUSP_ENTRY_RRIP_NM 4 #define SUSP_ENTRY_RRIP_CL 5 #define SUSP_ENTRY_RRIP_PL 6 #define SUSP_ENTRY_RRIP_RE 7 #define SUSP_ENTRY_RRIP_TF 8 #define SUSP_ENTRY_RRIP_SF 9 #define SUSP_RRIP_ER_EXT_ID "IEEE_P1282" #define SUSP_RRIP_ER_EXT_DES "THE IEEE P1282 PROTOCOL PROVIDES SUPPORT FOR POSIX FILE SYSTEM SEMANTICS." #define SUSP_RRIP_ER_EXT_SRC "PLEASE CONTACT THE IEEE STANDARDS DEPARTMENT, PISCATAWAY, NJ, USA FOR THE P1282 SPECIFICATION." #define SL_FLAGS_NONE 0 #define SL_FLAGS_CONTINUE 1 #define SL_FLAGS_CURRENT 2 #define SL_FLAGS_PARENT 4 #define SL_FLAGS_ROOT 8 typedef struct { ISO_SUSP_HEADER h; u_char mode [ISODCL(5,12)]; u_char links [ISODCL(13,20)]; u_char uid [ISODCL(21,28)]; u_char gid [ISODCL(29,36)]; u_char serial [ISODCL(37,44)]; } ISO_RRIP_PX; typedef struct { ISO_SUSP_HEADER h; u_char high [ISODCL(5,12)]; u_char low [ISODCL(13,20)]; } ISO_RRIP_PN; typedef struct { ISO_SUSP_HEADER h; u_char flags [ISODCL ( 4, 4)]; u_char component [ISODCL ( 4, 256)]; u_int nBytes; } ISO_RRIP_SL; typedef struct { ISO_SUSP_HEADER h; u_char flags [ISODCL ( 4, 4)]; u_char timestamp [ISODCL ( 5, 256)]; } ISO_RRIP_TF; #define RRIP_NM_FLAGS_NONE 0x00 #define RRIP_NM_FLAGS_CONTINUE 0x01 #define RRIP_NM_FLAGS_CURRENT 0x02 #define RRIP_NM_FLAGS_PARENT 0x04 typedef struct { ISO_SUSP_HEADER h; u_char flags [ISODCL ( 4, 4)]; u_char altname [ISODCL ( 4, 256)]; } ISO_RRIP_NM; /* Note that this is the same structure as cd9660_rrip.h : ISO_RRIP_CONT */ typedef struct { ISO_SUSP_HEADER h; u_char ca_sector [ISODCL ( 5, 12)]; u_char offset [ISODCL ( 13, 20)]; u_char length [ISODCL ( 21, 28)]; } ISO_SUSP_CE; typedef struct { ISO_SUSP_HEADER h; u_char padding_area [ISODCL ( 4, 256)]; } ISO_SUSP_PD; typedef struct { ISO_SUSP_HEADER h; u_char check [ISODCL ( 4, 5)]; u_char len_skp [ISODCL ( 6, 6)]; } ISO_SUSP_SP; typedef struct { ISO_SUSP_HEADER h; } ISO_SUSP_ST; typedef struct { ISO_SUSP_HEADER h; u_char len_id [ISODCL ( 4, 4)]; u_char len_des [ISODCL ( 5, 5)]; u_char len_src [ISODCL ( 6, 6)]; u_char ext_ver [ISODCL ( 7, 7)]; u_char ext_data [ISODCL (8,256)]; /* u_char ext_id [ISODCL ( 8, 256)]; u_char ext_des [ISODCL ( 257, 513)]; u_char ext_src [ISODCL ( 514, 770)];*/ } ISO_SUSP_ER; typedef struct { ISO_SUSP_HEADER h; u_char ext_seq [ISODCL ( 4, 4)]; } ISO_SUSP_ES; typedef union { ISO_RRIP_PX PX; ISO_RRIP_PN PN; ISO_RRIP_SL SL; ISO_RRIP_NM NM; ISO_RRIP_CLINK CL; ISO_RRIP_PLINK PL; ISO_RRIP_RELDIR RE; ISO_RRIP_TF TF; } rrip_entry; typedef union { ISO_SUSP_CE CE; ISO_SUSP_PD PD; ISO_SUSP_SP SP; ISO_SUSP_ST ST; ISO_SUSP_ER ER; ISO_SUSP_ES ES; } susp_entry; typedef union { susp_entry su_entry; rrip_entry rr_entry; } SUSP_ENTRIES; struct ISO_SUSP_ATTRIBUTES { SUSP_ENTRIES attr; int type; char type_of[2]; char last_in_suf; /* last entry in the System Use Field? */ /* Dan's addons - will merge later. This allows use of a switch */ char susp_type; /* SUSP or RRIP */ char entry_type; /* Record type */ char write_location; TAILQ_ENTRY(ISO_SUSP_ATTRIBUTES) rr_ll; }; #define CD9660_SUSP_ENTRY_SIZE(entry)\ ((int) ((entry)->attr.su_entry.SP.h.length[0])) /* Recursive function - move later to func pointer code*/ int cd9660_susp_finalize(cd9660node *); /* These two operate on single nodes */ int cd9660_susp_finalize_node(cd9660node *); int cd9660_rrip_finalize_node(cd9660node *); /* POSIX File attribute */ int cd9660node_rrip_px(struct ISO_SUSP_ATTRIBUTES *, fsnode *); /* Device number */ int cd9660node_rrip_pn(struct ISO_SUSP_ATTRIBUTES *, fsnode *); /* Symbolic link */ int cd9660node_rrip_SL(struct ISO_SUSP_ATTRIBUTES *, fsnode *); /* Alternate Name function */ void cd9660_rrip_NM(cd9660node *); void cd9660_rrip_add_NM(cd9660node *,const char *); /* Parent and child link function */ int cd9660_rrip_PL(struct ISO_SUSP_ATTRIBUTES *, cd9660node *); int cd9660_rrip_CL(struct ISO_SUSP_ATTRIBUTES *, cd9660node *); int cd9660_rrip_RE(struct ISO_SUSP_ATTRIBUTES *, cd9660node *); int cd9660node_rrip_tf(struct ISO_SUSP_ATTRIBUTES *, fsnode *); /* * Relocation directory function. I'm not quite sure what * sort of parameters are needed, but personally I don't think * any parameters are needed except for the memory address where * the information needs to be put in */ int cd9660node_rrip_re(void *, fsnode *); /* * Don't know if this function is needed because it apparently is an * optional feature that does not really need to be implemented but I * thought I should add it anyway. */ int cd9660_susp_ce (struct ISO_SUSP_ATTRIBUTES *, cd9660node *); int cd9660_susp_pd (struct ISO_SUSP_ATTRIBUTES *, int); int cd9660_susp_sp (struct ISO_SUSP_ATTRIBUTES *, cd9660node *); int cd9660_susp_st (struct ISO_SUSP_ATTRIBUTES *, cd9660node *); struct ISO_SUSP_ATTRIBUTES *cd9660_susp_ER(cd9660node *, u_char, const char *, const char *, const char *); struct ISO_SUSP_ATTRIBUTES *cd9660_susp_ES(struct ISO_SUSP_ATTRIBUTES*, cd9660node *); /* Helper functions */ /* Common SUSP/RRIP functions */ int cd9660_susp_initialize(cd9660node *, cd9660node *, cd9660node *, int); int cd9660_susp_initialize_node(cd9660node *); struct ISO_SUSP_ATTRIBUTES *cd9660node_susp_create_node(int, int, const char *, int); struct ISO_SUSP_ATTRIBUTES *cd9660node_susp_add_entry(cd9660node *, struct ISO_SUSP_ATTRIBUTES *, struct ISO_SUSP_ATTRIBUTES *, int); /* RRIP specific functions */ int cd9660_rrip_initialize_node(cd9660node *, cd9660node *, cd9660node *); void cd9660_createSL(cd9660node *); /* Functions that probably can be removed */ /* int cd9660node_initialize_node(int, char *); */ /* Global variables */ extern int rrip_squash; #endif makefs/src/usr.sbin/makefs/ffs.c010064400000000000000000001035431341414422000137760ustar00/* $NetBSD: ffs.c,v 1.44 2009/04/28 22:49:26 joerg Exp $ */ /* * Copyright (c) 2001 Wasabi Systems, Inc. * All rights reserved. * * Written by Luke Mewburn 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. */ /* * Copyright (c) 2009, 2010, 2012 * Thorsten Glaser * Copyright (c) 1982, 1986, 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. * * @(#)ffs_alloc.c 8.19 (Berkeley) 7/13/95 */ #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #include #if defined(__RCSID) && !defined(__lint) __IDSTRING(mbsdid, "$MirOS: src/usr.sbin/makefs/ffs.c,v 1.15 2019/01/05 15:08:37 tg Exp $"); __RCSID("$NetBSD: ffs.c,v 1.44 2009/04/28 22:49:26 joerg Exp $"); #endif /* !__lint */ #include #include #if !HAVE_NBTOOL_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include "makefs.h" #include "ffs.h" #if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS #include #endif #include #include #include #include #include "ffs/ufs_inode.h" #include "ffs/newfs_extern.h" #include "ffs/ffs_extern.h" #ifndef MAXPHYS #define MAXPHYS (64 * 1024) /* max raw I/O transfer size */ #endif #undef DIP #define DIP(dp, field) \ ((ffs_opts->version == 1) ? \ (dp)->ffs1_din.di_##field : (dp)->ffs2_din.di_##field) /* * Various filesystem defaults (cribbed from newfs(8)). */ #define DFL_FRAGSIZE 1024 /* fragment size */ #define DFL_BLKSIZE 8192 /* block size */ #define DFL_SECSIZE 512 /* sector size */ #define DFL_CYLSPERGROUP 65536 /* cylinders per group */ #define DFL_FRAGSPERINODE 4 /* fragments per inode */ #define DFL_ROTDELAY 0 /* rotational delay */ #define DFL_NRPOS 1 /* rotational positions */ #define DFL_RPM 3600 /* rpm of disk */ #define DFL_NSECTORS 64 /* # of sectors */ #define DFL_NTRACKS 16 /* # of tracks */ typedef struct { u_char *buf; /* buf for directory */ doff_t size; /* full size of buf */ doff_t cur; /* offset of current entry */ } dirbuf_t; static int ffs_create_image(const char *, fsinfo_t *); static void ffs_dump_fsinfo(fsinfo_t *); static void ffs_dump_dirbuf(dirbuf_t *, const char *, int); static void ffs_make_dirbuf(dirbuf_t *, const char *, fsnode *, int); static int ffs_populate_dir(const char *, fsnode *, fsinfo_t *); static void ffs_size_dir(fsnode *, fsinfo_t *); static void ffs_validate(const char *, fsnode *, fsinfo_t *); static void ffs_write_file(union dinode *, uint32_t, void *, fsinfo_t *); static void ffs_write_inode(union dinode *, uint32_t, const fsinfo_t *); static void *ffs_build_dinode1(struct ufs1_dinode *, dirbuf_t *, fsnode *, fsnode *, fsinfo_t *); static void *ffs_build_dinode2(struct ufs2_dinode *, dirbuf_t *, fsnode *, fsnode *, fsinfo_t *); int sectorsize; /* XXX: for buf.c::getblk() */ /* publically visible functions */ void ffs_prep_opts(fsinfo_t *fsopts) { ffs_opt_t *ffs_opts; if ((ffs_opts = calloc(1, sizeof(ffs_opt_t))) == NULL) err(1, "Allocating memory for ffs_options"); fsopts->fs_specific = ffs_opts; ffs_opts->bsize= -1; ffs_opts->fsize= -1; ffs_opts->cpg= -1; ffs_opts->density= -1; ffs_opts->minfree= -1; ffs_opts->optimization= -1; ffs_opts->maxcontig= -1; ffs_opts->maxbpg= -1; ffs_opts->avgfilesize= -1; ffs_opts->avgfpdir= -1; ffs_opts->version = 1; } void ffs_cleanup_opts(fsinfo_t *fsopts) { if (fsopts->fs_specific) free(fsopts->fs_specific); } int ffs_parse_opts(const char *option, fsinfo_t *fsopts) { ffs_opt_t *ffs_opts = fsopts->fs_specific; option_t ffs_options[] = { { "bsize", &ffs_opts->bsize, 1, INT_MAX, "block size" }, { "fsize", &ffs_opts->fsize, 1, INT_MAX, "fragment size" }, { "density", &ffs_opts->density, 1, INT_MAX, "bytes per inode" }, { "minfree", &ffs_opts->minfree, 0, 99, "minfree" }, { "maxbpf", &ffs_opts->maxbpg, 1, INT_MAX, "max blocks per file in a cg" }, { "avgfilesize", &ffs_opts->avgfilesize,1, INT_MAX, "expected average file size" }, { "avgfpdir", &ffs_opts->avgfpdir, 1, INT_MAX, "expected # of files per directory" }, { "extent", &ffs_opts->maxbsize, 1, INT_MAX, "maximum # extent size" }, { "maxbpcg", &ffs_opts->maxblkspercg,1, INT_MAX, "max # of blocks per group" }, { "version", &ffs_opts->version, 1, 2, "UFS version" }, { .name = NULL } }; char *var, *val; int rv; assert(option != NULL); assert(fsopts != NULL); assert(ffs_opts != NULL); if (debug & DEBUG_FS_PARSE_OPTS) printf("ffs_parse_opts: got `%s'\n", option); if ((var = strdup(option)) == NULL) err(1, "Allocating memory for copy of option string"); rv = 0; if ((val = strchr(var, '=')) == NULL) { warnx("Option `%s' doesn't contain a value", var); goto leave_ffs_parse_opts; } *val++ = '\0'; if (strcmp(var, "optimization") == 0) { if (strcmp(val, "time") == 0) { ffs_opts->optimization = FS_OPTTIME; } else if (strcmp(val, "space") == 0) { ffs_opts->optimization = FS_OPTSPACE; } else { warnx("Invalid optimization `%s'", val); goto leave_ffs_parse_opts; } rv = 1; } else rv = set_option(ffs_options, var, val); leave_ffs_parse_opts: if (var) free(var); return (rv); } void ffs_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts) { struct fs *superblock; struct timeval start; assert(image != NULL); assert(dir != NULL); assert(root != NULL); assert(fsopts != NULL); if (debug & DEBUG_FS_MAKEFS) printf("ffs_makefs: image %s directory %s root %p\n", image, dir, root); /* validate tree and options */ TIMER_START(start); ffs_validate(dir, root, fsopts); TIMER_RESULTS(start, "ffs_validate"); printf("Calculated size of `%s': %lld bytes, %lld inodes\n", image, (long long)fsopts->size, (long long)fsopts->inodes); /* create image */ TIMER_START(start); if (ffs_create_image(image, fsopts) == -1) errx(1, "Image file `%s' not created.", image); TIMER_RESULTS(start, "ffs_create_image"); fsopts->curinode = ROOTINO; if (debug & DEBUG_FS_MAKEFS) putchar('\n'); /* populate image */ printf("Populating `%s'\n", image); TIMER_START(start); if (! ffs_populate_dir(dir, root, fsopts)) errx(1, "Image file `%s' not populated.", image); TIMER_RESULTS(start, "ffs_populate_dir"); /* ensure no outstanding buffers remain */ if (debug & DEBUG_FS_MAKEFS) bcleanup(); /* update various superblock parameters */ superblock = fsopts->superblock; superblock->fs_fmod = 0; superblock->fs_old_cstotal.cs_ndir = superblock->fs_cstotal.cs_ndir; superblock->fs_old_cstotal.cs_nbfree = superblock->fs_cstotal.cs_nbfree; superblock->fs_old_cstotal.cs_nifree = superblock->fs_cstotal.cs_nifree; superblock->fs_old_cstotal.cs_nffree = superblock->fs_cstotal.cs_nffree; /* write out superblock; image is now complete */ ffs_write_superblock(fsopts->superblock, fsopts); if (close(fsopts->fd) == -1) err(1, "Closing `%s'", image); fsopts->fd = -1; printf("Image `%s' complete\n", image); } /* end of public functions */ static void ffs_validate(const char *dir, fsnode *root, fsinfo_t *fsopts) { int32_t ncg = 1; #ifdef notyet int32_t spc, nspf, ncyl, fssize; #endif ffs_opt_t *ffs_opts = fsopts->fs_specific; assert(dir != NULL); assert(root != NULL); assert(fsopts != NULL); assert(ffs_opts != NULL); if (debug & DEBUG_FS_VALIDATE) { printf("ffs_validate: before defaults set:\n"); ffs_dump_fsinfo(fsopts); } /* set FFS defaults */ if (fsopts->sectorsize == -1) fsopts->sectorsize = DFL_SECSIZE; if (ffs_opts->fsize == -1) ffs_opts->fsize = MAX(DFL_FRAGSIZE, fsopts->sectorsize); if (ffs_opts->bsize == -1) ffs_opts->bsize = MIN(DFL_BLKSIZE, 8 * ffs_opts->fsize); if (ffs_opts->cpg == -1) ffs_opts->cpg = DFL_CYLSPERGROUP; else ffs_opts->cpgflg = 1; /* fsopts->density is set below */ if (ffs_opts->nsectors == -1) ffs_opts->nsectors = DFL_NSECTORS; if (ffs_opts->minfree == -1) ffs_opts->minfree = MINFREE; if (ffs_opts->optimization == -1) ffs_opts->optimization = DEFAULTOPT; if (ffs_opts->maxcontig == -1) ffs_opts->maxcontig = MAX(1, MIN(MAXPHYS, FFS_MAXBSIZE) / ffs_opts->bsize); /* XXX ondisk32 */ if (ffs_opts->maxbpg == -1) ffs_opts->maxbpg = ffs_opts->bsize / sizeof(int32_t); if (ffs_opts->avgfilesize == -1) ffs_opts->avgfilesize = AVFILESIZ; if (ffs_opts->avgfpdir == -1) ffs_opts->avgfpdir = AFPDIR; /* calculate size of tree */ ffs_size_dir(root, fsopts); fsopts->inodes += ROOTINO; /* include first two inodes */ if (debug & DEBUG_FS_VALIDATE) printf("ffs_validate: size of tree: %lld bytes, %lld inodes\n", (long long)fsopts->size, (long long)fsopts->inodes); /* add requested slop */ fsopts->size += fsopts->freeblocks; fsopts->inodes += fsopts->freefiles; if (fsopts->freefilepc > 0) fsopts->inodes = fsopts->inodes * (100 + fsopts->freefilepc) / 100; if (fsopts->freeblockpc > 0) fsopts->size = fsopts->size * (100 + fsopts->freeblockpc) / 100; /* add space needed for superblocks */ /* * The old SBOFF (SBLOCK_UFS1) is used here because makefs is * typically used for small filesystems where space matters. * XXX make this an option. */ fsopts->size += (SBLOCK_UFS1 + SBLOCKSIZE) * ncg; /* add space needed to store inodes, x3 for blockmaps, etc */ if (ffs_opts->version == 1) fsopts->size += ncg * DINODE1_SIZE * roundup(fsopts->inodes / ncg, ffs_opts->bsize / DINODE1_SIZE); else fsopts->size += ncg * DINODE2_SIZE * roundup(fsopts->inodes / ncg, ffs_opts->bsize / DINODE2_SIZE); /* add minfree */ if (ffs_opts->minfree > 0) fsopts->size = fsopts->size * (100 + ffs_opts->minfree) / 100; /* * XXX any other fs slop to add, such as csum's, bitmaps, etc ?? */ if (fsopts->size < fsopts->minsize) /* ensure meets minimum size */ fsopts->size = fsopts->minsize; /* round up to the next block */ fsopts->size = roundup(fsopts->size, ffs_opts->bsize); /* calculate density if necessary */ if (ffs_opts->density == -1) ffs_opts->density = fsopts->size / fsopts->inodes + 1; if (debug & DEBUG_FS_VALIDATE) { printf("ffs_validate: after defaults set:\n"); ffs_dump_fsinfo(fsopts); printf("ffs_validate: dir %s; %lld bytes, %lld inodes\n", dir, (long long)fsopts->size, (long long)fsopts->inodes); } sectorsize = fsopts->sectorsize; /* XXX - see earlier */ /* now check calculated sizes vs requested sizes */ if (fsopts->maxsize > 0 && fsopts->size > fsopts->maxsize) { errx(1, "`%s' size of %lld is larger than the maxsize of %lld.", dir, (long long)fsopts->size, (long long)fsopts->maxsize); } } static void ffs_dump_fsinfo(fsinfo_t *f) { ffs_opt_t *fs = f->fs_specific; printf("fsopts at %p\n", f); printf("\tsize %lld, inodes %lld, curinode %u\n", (long long)f->size, (long long)f->inodes, f->curinode); printf("\tminsize %lld, maxsize %lld\n", (long long)f->minsize, (long long)f->maxsize); printf("\tfree files %lld, freefile %% %d\n", (long long)f->freefiles, f->freefilepc); printf("\tfree blocks %lld, freeblock %% %d\n", (long long)f->freeblocks, f->freeblockpc); printf("\tneedswap %d, sectorsize %d\n", f->needswap, f->sectorsize); printf("\tbsize %d, fsize %d, cpg %d, density %d\n", fs->bsize, fs->fsize, fs->cpg, fs->density); printf("\tnsectors %d, rpm %d, minfree %d\n", fs->nsectors, fs->rpm, fs->minfree); printf("\tmaxcontig %d, maxbpg %d\n", fs->maxcontig, fs->maxbpg); printf("\toptimization %s\n", fs->optimization == FS_OPTSPACE ? "space" : "time"); } static int ffs_create_image(const char *image, fsinfo_t *fsopts) { #if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS struct statvfs sfs; #endif struct fs *fs; char *buf; int i, bufsize; off_t bufrem; assert (image != NULL); assert (fsopts != NULL); /* create image */ if ((fsopts->fd = open(image, O_RDWR | O_CREAT | O_TRUNC, 0666)) == -1) { warn("Can't open `%s' for writing", image); return (-1); } /* zero image */ #if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS if (fstatvfs(fsopts->fd, &sfs) == -1) { #endif bufsize = 8192; #if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS warn("can't fstatvfs `%s', using default %d byte chunk", image, bufsize); } else bufsize = sfs.f_iosize; #endif bufrem = fsopts->size; if (debug & DEBUG_FS_CREATE_IMAGE) printf( "zero-ing image `%s', %lld sectors, using %d byte chunks\n", image, (long long)bufrem, bufsize); if ((buf = calloc(1, bufsize)) == NULL) { warn("Can't create buffer for sector"); return (-1); } while (bufrem > 0) { i = write(fsopts->fd, buf, MIN(bufsize, bufrem)); if (i == -1) { warn("zeroing image, %lld bytes to go", (long long)bufrem); free(buf); return (-1); } bufrem -= i; } free(buf); /* make the filesystem */ if (debug & DEBUG_FS_CREATE_IMAGE) printf("calling mkfs(\"%s\", ...)\n", image); fs = ffs_mkfs(image, fsopts); fsopts->superblock = (void *)fs; if (debug & DEBUG_FS_CREATE_IMAGE) { time_t t; t = (time_t)((struct fs *)fsopts->superblock)->fs_time; printf("mkfs returned %p; fs_time %s", fsopts->superblock, ctime(&t)); printf("fs totals: nbfree %lld, nffree %lld, nifree %lld, ndir %lld\n", (long long)fs->fs_cstotal.cs_nbfree, (long long)fs->fs_cstotal.cs_nffree, (long long)fs->fs_cstotal.cs_nifree, (long long)fs->fs_cstotal.cs_ndir); } if (fs->fs_cstotal.cs_nifree + ROOTINO < fsopts->inodes) { warnx( "Image file `%s' has %lld free inodes; %lld are required.", image, (long long)(fs->fs_cstotal.cs_nifree + ROOTINO), (long long)fsopts->inodes); return (-1); } return (fsopts->fd); } static void ffs_size_dir(fsnode *root, fsinfo_t *fsopts) { struct direct tmpdir; fsnode * node; int curdirsize, this; ffs_opt_t *ffs_opts = fsopts->fs_specific; /* node may be NULL (empty directory) */ assert(fsopts != NULL); assert(ffs_opts != NULL); if (debug & DEBUG_FS_SIZE_DIR) printf("ffs_size_dir: entry: bytes %lld inodes %lld\n", (long long)fsopts->size, (long long)fsopts->inodes); #define ADDDIRENT(e) do { \ tmpdir.d_namlen = strlen((e)); \ this = DIRSIZ(0, &tmpdir, 0); \ if (debug & DEBUG_FS_SIZE_DIR_ADD_DIRENT) \ printf("ADDDIRENT: was: %s (%d) this %d cur %d\n", \ e, tmpdir.d_namlen, this, curdirsize); \ if (this + curdirsize > roundup(curdirsize, DIRBLKSIZ)) \ curdirsize = roundup(curdirsize, DIRBLKSIZ); \ curdirsize += this; \ if (debug & DEBUG_FS_SIZE_DIR_ADD_DIRENT) \ printf("ADDDIRENT: now: %s (%d) this %d cur %d\n", \ e, tmpdir.d_namlen, this, curdirsize); \ } while (0); /* * XXX this needs to take into account extra space consumed * by indirect blocks, etc. */ #define ADDSIZE(x) do { \ fsopts->size += roundup((x), ffs_opts->fsize); \ } while (0); curdirsize = 0; for (node = root; node != NULL; node = node->next) { ADDDIRENT(node->name); if (node == root) { /* we're at "." */ assert(strcmp(node->name, ".") == 0); ADDDIRENT(".."); } else if ((node->inode->flags & FI_SIZED) == 0) { /* don't count duplicate names */ node->inode->flags |= FI_SIZED; if (debug & DEBUG_FS_SIZE_DIR_NODE) printf("ffs_size_dir: `%s' size %lld\n", node->name, (long long)node->inode->st.st_size); fsopts->inodes++; if (node->type == S_IFREG) ADDSIZE(node->inode->st.st_size); if (node->type == S_IFLNK) { size_t slen; slen = strlen(node->symlink) + 1; if (slen >= (ffs_opts->version == 1 ? MAXSYMLINKLEN_UFS1 : MAXSYMLINKLEN_UFS2)) ADDSIZE(slen); } } if (node->type == S_IFDIR) ffs_size_dir(node->child, fsopts); } ADDSIZE(curdirsize); if (debug & DEBUG_FS_SIZE_DIR) printf("ffs_size_dir: exit: size %lld inodes %lld\n", (long long)fsopts->size, (long long)fsopts->inodes); } static void * ffs_build_dinode1(struct ufs1_dinode *dinp, dirbuf_t *dbufp, fsnode *cur, fsnode *root, fsinfo_t *fsopts) { size_t slen; void *membuf; memset(dinp, 0, sizeof(*dinp)); dinp->di_mode = cur->inode->st.st_mode; dinp->di_nlink = cur->inode->nlink; dinp->di_size = cur->inode->st.st_size; dinp->di_atime = cur->inode->st.st_atime; dinp->di_mtime = cur->inode->st.st_mtime; dinp->di_ctime = cur->inode->st.st_ctime; #if HAVE_STRUCT_STAT_ST_MTIMENSEC dinp->di_atimensec = cur->inode->st.st_atimensec; dinp->di_mtimensec = cur->inode->st.st_mtimensec; dinp->di_ctimensec = cur->inode->st.st_ctimensec; #endif /* if maxtime was given, clamp all timestamps to this */ if (fsopts->maxtime >= 0) { if (dinp->di_atime >= fsopts->maxtime) { dinp->di_atime = fsopts->maxtime; #if HAVE_STRUCT_STAT_ST_MTIMENSEC dinp->di_atimensec = 0; #endif } if (dinp->di_mtime >= fsopts->maxtime) { dinp->di_mtime = fsopts->maxtime; #if HAVE_STRUCT_STAT_ST_MTIMENSEC dinp->di_mtimensec = 0; #endif } if (dinp->di_ctime >= fsopts->maxtime) { dinp->di_ctime = fsopts->maxtime; #if HAVE_STRUCT_STAT_ST_MTIMENSEC dinp->di_ctimensec = 0; #endif } } #if HAVE_STRUCT_STAT_ST_FLAGS dinp->di_flags = cur->inode->st.st_flags; #endif #if HAVE_STRUCT_STAT_ST_GEN dinp->di_gen = cur->inode->st.st_gen; #endif dinp->di_uid = cur->inode->st.st_uid; dinp->di_gid = cur->inode->st.st_gid; /* not set: di_db, di_ib, di_blocks, di_spare */ membuf = NULL; if (cur == root) { /* "."; write dirbuf */ membuf = dbufp->buf; dinp->di_size = dbufp->size; } else if (S_ISBLK(cur->type) || S_ISCHR(cur->type)) { dinp->di_size = 0; /* a device */ dinp->di_rdev = ufs_rw32(cur->inode->st.st_rdev, fsopts->needswap); } else if (S_ISLNK(cur->type)) { /* symlink */ slen = strlen(cur->symlink); if (slen < MAXSYMLINKLEN_UFS1) { /* short link */ memcpy(dinp->di_db, cur->symlink, slen); } else membuf = cur->symlink; dinp->di_size = slen; } return membuf; } static void * ffs_build_dinode2(struct ufs2_dinode *dinp, dirbuf_t *dbufp, fsnode *cur, fsnode *root, fsinfo_t *fsopts) { size_t slen; void *membuf; memset(dinp, 0, sizeof(*dinp)); dinp->di_mode = cur->inode->st.st_mode; dinp->di_nlink = cur->inode->nlink; dinp->di_size = cur->inode->st.st_size; dinp->di_atime = cur->inode->st.st_atime; dinp->di_mtime = cur->inode->st.st_mtime; dinp->di_ctime = cur->inode->st.st_ctime; #if HAVE_STRUCT_STAT_ST_MTIMENSEC dinp->di_atimensec = cur->inode->st.st_atimensec; dinp->di_mtimensec = cur->inode->st.st_mtimensec; dinp->di_ctimensec = cur->inode->st.st_ctimensec; #endif #if HAVE_STRUCT_STAT_ST_FLAGS dinp->di_flags = cur->inode->st.st_flags; #endif #if HAVE_STRUCT_STAT_ST_GEN dinp->di_gen = cur->inode->st.st_gen; #endif #if HAVE_STRUCT_STAT_BIRTHTIME dinp->di_birthtime = cur->inode->st.st_birthtime; dinp->di_birthnsec = cur->inode->st.st_birthtimensec; #endif dinp->di_uid = cur->inode->st.st_uid; dinp->di_gid = cur->inode->st.st_gid; /* not set: di_db, di_ib, di_blocks, di_spare */ membuf = NULL; if (cur == root) { /* "."; write dirbuf */ membuf = dbufp->buf; dinp->di_size = dbufp->size; } else if (S_ISBLK(cur->type) || S_ISCHR(cur->type)) { dinp->di_size = 0; /* a device */ dinp->di_rdev = ufs_rw64(cur->inode->st.st_rdev, fsopts->needswap); } else if (S_ISLNK(cur->type)) { /* symlink */ slen = strlen(cur->symlink); if (slen < MAXSYMLINKLEN_UFS2) { /* short link */ memcpy(dinp->di_db, cur->symlink, slen); } else membuf = cur->symlink; dinp->di_size = slen; } return membuf; } static int ffs_populate_dir(const char *dir, fsnode *root, fsinfo_t *fsopts) { fsnode *cur; dirbuf_t dirbuf; union dinode din; void *membuf; char *path; ffs_opt_t *ffs_opts = fsopts->fs_specific; int rv = 0; assert(dir != NULL); assert(root != NULL); assert(fsopts != NULL); assert(ffs_opts != NULL); if ((path = malloc(maxpathlen + 1)) == NULL) err(1, "malloc"); (void)memset(&dirbuf, 0, sizeof(dirbuf)); if (debug & DEBUG_FS_POPULATE) printf("ffs_populate_dir: PASS 1 dir %s node %p\n", dir, root); /* * pass 1: allocate inode numbers, build directory `file' */ for (cur = root; cur != NULL; cur = cur->next) { if ((cur->inode->flags & FI_ALLOCATED) == 0) { cur->inode->flags |= FI_ALLOCATED; if (cur == root && cur->parent != NULL) cur->inode->ino = cur->parent->inode->ino; else { cur->inode->ino = fsopts->curinode; fsopts->curinode++; } } ffs_make_dirbuf(&dirbuf, cur->name, cur, fsopts->needswap); if (cur == root) { /* we're at "."; add ".." */ ffs_make_dirbuf(&dirbuf, "..", cur->parent == NULL ? cur : cur->parent->first, fsopts->needswap); root->inode->nlink++; /* count my parent's link */ } else if (cur->child != NULL) root->inode->nlink++; /* count my child's link */ /* * XXX possibly write file and long symlinks here, * ensuring that blocks get written before inodes? * otoh, this isn't a real filesystem, so who * cares about ordering? :-) */ } if (debug & DEBUG_FS_POPULATE_DIRBUF) ffs_dump_dirbuf(&dirbuf, dir, fsopts->needswap); /* * pass 2: write out dirbuf, then non-directories at this level */ if (debug & DEBUG_FS_POPULATE) printf("ffs_populate_dir: PASS 2 dir %s\n", dir); for (cur = root; cur != NULL; cur = cur->next) { if (cur->inode->flags & FI_WRITTEN) continue; /* skip hard-linked entries */ cur->inode->flags |= FI_WRITTEN; if ((size_t)snprintf(path, maxpathlen, "%s/%s", dir, cur->name) >= maxpathlen) errx(1, "Pathname too long."); if (cur->child != NULL) continue; /* child creates own inode */ /* build on-disk inode */ if (ffs_opts->version == 1) membuf = ffs_build_dinode1(&din.ffs1_din, &dirbuf, cur, root, fsopts); else membuf = ffs_build_dinode2(&din.ffs2_din, &dirbuf, cur, root, fsopts); if (debug & DEBUG_FS_POPULATE_NODE) { printf("ffs_populate_dir: writing ino %d, %s", cur->inode->ino, inode_type(cur->type)); if (cur->inode->nlink > 1) printf(", nlink %d", cur->inode->nlink); putchar('\n'); } if (membuf != NULL) { ffs_write_file(&din, cur->inode->ino, membuf, fsopts); } else if (S_ISREG(cur->type)) { ffs_write_file(&din, cur->inode->ino, path, fsopts); } else { assert (! S_ISDIR(cur->type)); ffs_write_inode(&din, cur->inode->ino, fsopts); } } /* * pass 3: write out sub-directories */ if (debug & DEBUG_FS_POPULATE) printf("ffs_populate_dir: PASS 3 dir %s\n", dir); for (cur = root; cur != NULL; cur = cur->next) { if (cur->child == NULL) continue; if ((size_t)snprintf(path, maxpathlen, "%s/%s", dir, cur->name) >= maxpathlen) errx(1, "Pathname too long."); if (! ffs_populate_dir(path, cur->child, fsopts)) goto out; } if (debug & DEBUG_FS_POPULATE) printf("ffs_populate_dir: DONE dir %s\n", dir); /* cleanup */ if (dirbuf.buf != NULL) free(dirbuf.buf); rv = 1; out: free(path); return (rv); } static void ffs_write_file(union dinode *din, uint32_t ino, void *buf, fsinfo_t *fsopts) { int isfile, ffd; char *fbuf, *p; off_t bufleft, chunk, offset; ssize_t nread; struct inode in; struct buf * bp; ffs_opt_t *ffs_opts = fsopts->fs_specific; assert (din != NULL); assert (buf != NULL); assert (fsopts != NULL); assert (ffs_opts != NULL); isfile = S_ISREG(DIP(din, mode)); fbuf = NULL; ffd = -1; p = NULL; in.i_fs = (struct fs *)fsopts->superblock; if (debug & DEBUG_FS_WRITE_FILE) { printf( "ffs_write_file: ino %u, din %p, isfile %d, %s, size %lld", ino, din, isfile, inode_type(DIP(din, mode) & S_IFMT), (long long)DIP(din, size)); if (isfile) printf(", file '%s'\n", (char *)buf); else printf(", buffer %p\n", buf); } in.i_number = ino; in.i_size = DIP(din, size); if (ffs_opts->version == 1) memcpy(&in.i_din.ffs1_din, &din->ffs1_din, sizeof(in.i_din.ffs1_din)); else memcpy(&in.i_din.ffs2_din, &din->ffs2_din, sizeof(in.i_din.ffs2_din)); in.i_fd = fsopts->fd; if (DIP(din, size) == 0) goto write_inode_and_leave; /* mmm, cheating */ if (isfile) { if ((fbuf = malloc(ffs_opts->bsize)) == NULL) err(1, "Allocating memory for write buffer"); if ((ffd = open((char *)buf, O_RDONLY, 0444)) == -1) { warn("Can't open `%s' for reading", (char *)buf); goto leave_ffs_write_file; } } else { p = buf; } chunk = 0; for (bufleft = DIP(din, size); bufleft > 0; bufleft -= chunk) { chunk = MIN(bufleft, ffs_opts->bsize); if (!isfile) ; else if ((nread = read(ffd, fbuf, chunk)) == -1) err(EXIT_FAILURE, "Reading `%s', %lld bytes to go", (char *)buf, (long long)bufleft); else if (nread != chunk) errx(EXIT_FAILURE, "Reading `%s', %lld bytes to go, " "read %zd bytes, expected %ju bytes, does " "metalog size= attribute mismatch source size?", (char *)buf, (long long)bufleft, nread, (uintmax_t)chunk); else p = fbuf; offset = DIP(din, size) - bufleft; if (debug & DEBUG_FS_WRITE_FILE_BLOCK) printf( "ffs_write_file: write %p offset %lld size %lld left %lld\n", p, (long long)offset, (long long)chunk, (long long)bufleft); /* * XXX if holey support is desired, do the check here * * XXX might need to write out last bit in fragroundup * sized chunk. however, ffs_balloc() handles this for us */ errno = ffs_balloc(&in, offset, chunk, &bp); bad_ffs_write_file: if (errno != 0) err(1, "Writing inode %d (%s), bytes %lld + %lld", ino, isfile ? (char *)buf : inode_type(DIP(din, mode) & S_IFMT), (long long)offset, (long long)chunk); memcpy(bp->b_data, p, chunk); errno = bwrite(bp); if (errno != 0) goto bad_ffs_write_file; brelse(bp); if (!isfile) p += chunk; } write_inode_and_leave: ffs_write_inode(&in.i_din, in.i_number, fsopts); leave_ffs_write_file: if (fbuf) free(fbuf); if (ffd != -1) close(ffd); } static void ffs_dump_dirbuf(dirbuf_t *dbuf, const char *dir, int needswap) { doff_t i; struct direct *de; uint16_t reclen; assert (dbuf != NULL); assert (dir != NULL); printf("ffs_dump_dirbuf: dir %s size %d cur %d\n", dir, dbuf->size, dbuf->cur); for (i = 0; i < dbuf->size; ) { de = (struct direct *)(dbuf->buf + i); reclen = ufs_rw16(de->d_reclen, needswap); printf( " inode %4d %7s offset %4d reclen %3d namlen %3d name %s\n", ufs_rw32(de->d_fileno, needswap), inode_type(DTTOIF(de->d_type)), i, reclen, de->d_namlen, de->d_name); i += reclen; assert(reclen > 0); } } static void ffs_make_dirbuf(dirbuf_t *dbuf, const char *name, fsnode *node, int needswap) { struct direct de, *dp; uint16_t llen, reclen; u_char *newbuf; assert (dbuf != NULL); assert (name != NULL); assert (node != NULL); /* create direct entry */ (void)memset(&de, 0, sizeof(de)); de.d_fileno = ufs_rw32(node->inode->ino, needswap); de.d_type = IFTODT(node->type); de.d_namlen = (uint8_t)strlen(name); strlcpy(de.d_name, name, sizeof (de.d_name)); reclen = DIRSIZ(0, &de, needswap); de.d_reclen = ufs_rw16(reclen, needswap); dp = (struct direct *)(dbuf->buf + dbuf->cur); llen = 0; if (dp != NULL) llen = DIRSIZ(0, dp, needswap); if (debug & DEBUG_FS_MAKE_DIRBUF) printf( "ffs_make_dirbuf: dbuf siz %d cur %d lastlen %d\n" " ino %d type %d reclen %d namlen %d name %.30s\n", dbuf->size, dbuf->cur, llen, ufs_rw32(de.d_fileno, needswap), de.d_type, reclen, de.d_namlen, de.d_name); if (reclen + dbuf->cur + llen > roundup(dbuf->size, DIRBLKSIZ)) { if (debug & DEBUG_FS_MAKE_DIRBUF) printf("ffs_make_dirbuf: growing buf to %d\n", dbuf->size + DIRBLKSIZ); if ((newbuf = realloc(dbuf->buf, dbuf->size + DIRBLKSIZ)) == NULL) err(1, "Allocating memory for directory buffer"); dbuf->buf = newbuf; dbuf->size += DIRBLKSIZ; memset(dbuf->buf + dbuf->size - DIRBLKSIZ, 0, DIRBLKSIZ); dbuf->cur = dbuf->size - DIRBLKSIZ; } else if (dp) { /* shrink end of previous */ dp->d_reclen = ufs_rw16(llen,needswap); dbuf->cur += llen; } dp = (struct direct *)(dbuf->buf + dbuf->cur); memcpy(dp, &de, reclen); dp->d_reclen = ufs_rw16(dbuf->size - dbuf->cur, needswap); } /* * cribbed from sys/ufs/ffs/ffs_alloc.c */ static void ffs_write_inode(union dinode *dp, uint32_t ino, const fsinfo_t *fsopts) { char *buf; struct ufs1_dinode *dp1; struct ufs2_dinode *dp2, *dip; struct cg *cgp; struct fs *fs; int cg, i; unsigned cgino; daddr_t d; char sbbuf[FFS_MAXBSIZE]; uint32_t initediblk; ffs_opt_t *ffs_opts = fsopts->fs_specific; assert (dp != NULL); assert (ino > 0); assert (fsopts != NULL); assert (ffs_opts != NULL); fs = (struct fs *)fsopts->superblock; cg = ino_to_cg(fs, ino); cgino = ino % fs->fs_ipg; if (debug & DEBUG_FS_WRITE_INODE) printf("ffs_write_inode: din %p ino %u cg %d cgino %d\n", dp, ino, cg, cgino); ffs_rdfs(fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &sbbuf, fsopts); cgp = (struct cg *)sbbuf; if (!cg_chkmagic(cgp, fsopts->needswap)) errx(1, "ffs_write_inode: cg %d: bad magic number", cg); assert (isclr(cg_inosused(cgp, fsopts->needswap), cgino)); buf = malloc(fs->fs_bsize); if (buf == NULL) errx(1, "ffs_write_inode: cg %d: can't alloc inode block", cg); dp1 = (struct ufs1_dinode *)buf; dp2 = (struct ufs2_dinode *)buf; if (fs->fs_cstotal.cs_nifree == 0) errx(1, "ffs_write_inode: fs out of inodes for ino %u", ino); if (fs->fs_cs(fs, cg).cs_nifree == 0) errx(1, "ffs_write_inode: cg %d out of inodes for ino %u", cg, ino); setbit(cg_inosused(cgp, fsopts->needswap), cgino); ufs_add32(cgp->cg_cs.cs_nifree, -1, fsopts->needswap); fs->fs_cstotal.cs_nifree--; fs->fs_cs(fs, cg).cs_nifree--; if (S_ISDIR(DIP(dp, mode))) { ufs_add32(cgp->cg_cs.cs_ndir, 1, fsopts->needswap); fs->fs_cstotal.cs_ndir++; fs->fs_cs(fs, cg).cs_ndir++; } /* * Initialize inode blocks on the fly for UFS2. */ initediblk = ufs_rw32(cgp->cg_initediblk, fsopts->needswap); if (ffs_opts->version == 2 && cgino + INOPB(fs) > initediblk && initediblk < ufs_rw32(cgp->cg_niblk, fsopts->needswap)) { memset(buf, 0, fs->fs_bsize); dip = (struct ufs2_dinode *)buf; #ifndef __MirBSD__ srandom(time(NULL)); #endif for (i = 0; i < INOPB(fs); i++) { #ifdef __MirBSD__ dip->di_gen = arc4random() / 4 + 1; #else dip->di_gen = random() / 2 + 1; #endif dip++; } ffs_wtfs(fsbtodb(fs, ino_to_fsba(fs, cg * fs->fs_ipg + initediblk)), fs->fs_bsize, buf, fsopts); initediblk += INOPB(fs); cgp->cg_initediblk = ufs_rw32(initediblk, fsopts->needswap); } ffs_wtfs(fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &sbbuf, fsopts); /* now write inode */ d = fsbtodb(fs, ino_to_fsba(fs, ino)); ffs_rdfs(d, fs->fs_bsize, buf, fsopts); if (fsopts->needswap) { if (ffs_opts->version == 1) ffs_dinode1_swap(&dp->ffs1_din, &dp1[ino_to_fsbo(fs, ino)]); else ffs_dinode2_swap(&dp->ffs2_din, &dp2[ino_to_fsbo(fs, ino)]); } else { if (ffs_opts->version == 1) dp1[ino_to_fsbo(fs, ino)] = dp->ffs1_din; else dp2[ino_to_fsbo(fs, ino)] = dp->ffs2_din; } ffs_wtfs(d, fs->fs_bsize, buf, fsopts); free(buf); } void panic(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vwarnx(fmt, ap); va_end(ap); exit(1); } makefs/src/usr.sbin/makefs/ffs.h010064400000000000000000000054461314214553100140120ustar00/* $NetBSD: ffs.h,v 1.1 2004/12/20 20:51:42 jmc Exp $ */ /* * Copyright (c) 2001-2003 Wasabi Systems, Inc. * All rights reserved. * * Written by Luke Mewburn 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 _FFS_H #define _FFS_H typedef struct { int bsize; /* block size */ int fsize; /* fragment size */ int cpg; /* cylinders per group */ int cpgflg; /* cpg was specified by user */ int density; /* bytes per inode */ int ntracks; /* number of tracks */ int nsectors; /* number of sectors */ int rpm; /* rpm */ int minfree; /* free space threshold */ int optimization; /* optimization (space or time) */ int maxcontig; /* max contiguous blocks to allocate */ int rotdelay; /* rotational delay between blocks */ int maxbpg; /* maximum blocks per file in a cyl group */ int nrpos; /* # of distinguished rotational positions */ int avgfilesize; /* expected average file size */ int avgfpdir; /* expected # of files per directory */ int version; /* filesystem version (1 = FFS, 2 = UFS2) */ int maxbsize; /* maximum extent size */ int maxblkspercg; /* max # of blocks per cylinder group */ /* XXX: support `old' filesystems ? */ } ffs_opt_t; #endif /* _FFS_H */ makefs/src/usr.sbin/makefs/ffs/Makefile.inc010064400000000000000000000003271134453732000160450ustar00# $NetBSD: Makefile.inc,v 1.1 2009/01/16 19:39:52 pooka Exp $ # .PATH: ${.CURDIR}/ffs ${NETBSDSRCDIR}/sys/ufs/ffs SRCS+= ffs_alloc.c ffs_balloc.c ffs_bswap.c ffs_subr.c ffs_tables.c ufs_bmap.c SRCS+= buf.c mkfs.c makefs/src/usr.sbin/makefs/ffs/buf.c010064400000000000000000000151621134456207000145600ustar00/* $NetBSD: buf.c,v 1.12 2004/06/20 22:20:18 jmc Exp $ */ /* * Copyright (c) 2009 * Thorsten Glaser * Copyright (c) 2001 Wasabi Systems, Inc. * All rights reserved. * * Written by Luke Mewburn 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. */ #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #include #if defined(__RCSID) && !defined(__lint) __RCSID("$NetBSD: buf.c,v 1.12 2004/06/20 22:20:18 jmc Exp $"); __IDSTRING(mbsdid, "$MirOS: src/usr.sbin/makefs/ffs/buf.c,v 1.3 2010/03/06 23:24:16 tg Exp $"); #endif /* !__lint */ #include #include #include #include #include #include #include #include #include "makefs.h" #include #include #include "ffs/buf.h" #include "ffs/ufs_inode.h" extern int sectorsize; /* XXX: from ffs.c & mkfs.c */ TAILQ_HEAD(buftailhead,buf) buftail; int bread(int fd, struct fs *fs, daddr_t blkno, int size, struct buf **bpp) { off_t offset; ssize_t rv; assert (fs != NULL); assert (bpp != NULL); if (debug & DEBUG_BUF_BREAD) printf("bread: fs %p blkno %lld size %d\n", fs, (long long)blkno, size); *bpp = getblk(fd, fs, blkno, size); offset = (*bpp)->b_blkno * sectorsize; /* XXX */ if (debug & DEBUG_BUF_BREAD) printf("bread: bp %p blkno %lld offset %lld bcount %ld\n", (*bpp), (long long)(*bpp)->b_blkno, (long long) offset, (*bpp)->b_bcount); if (lseek((*bpp)->b_fd, offset, SEEK_SET) == -1) err(1, "bread: lseek %lld (%lld)", (long long)(*bpp)->b_blkno, (long long)offset); rv = read((*bpp)->b_fd, (*bpp)->b_data, (*bpp)->b_bcount); if (debug & DEBUG_BUF_BREAD) printf("bread: read %ld (%lld) returned %d\n", (*bpp)->b_bcount, (long long)offset, (int)rv); if (rv == -1) /* read error */ err(1, "bread: read %ld (%lld) returned %d", (*bpp)->b_bcount, (long long)offset, (int)rv); else if (rv != (*bpp)->b_bcount) /* short read */ err(1, "bread: read %ld (%lld) returned %d", (*bpp)->b_bcount, (long long)offset, (int)rv); else return (0); } void brelse(struct buf *bp) { assert (bp != NULL); assert (bp->b_data != NULL); if (bp->b_lblkno < 0) { /* * XXX don't remove any buffers with negative logical block * numbers (lblkno), so that we retain the mapping * of negative lblkno -> real blkno that ffs_balloc() * sets up. * * if we instead released these buffers, and implemented * ufs_strategy() (and ufs_bmaparray()) and called those * from bread() and bwrite() to convert the lblkno to * a real blkno, we'd add a lot more code & complexity * and reading off disk, for little gain, because this * simple hack works for our purpose. */ bp->b_bcount = 0; return; } TAILQ_REMOVE(&buftail, bp, b_tailq); free(bp->b_data); free(bp); } int bwrite(struct buf *bp) { off_t offset; ssize_t rv; assert (bp != NULL); offset = bp->b_blkno * sectorsize; /* XXX */ if (debug & DEBUG_BUF_BWRITE) printf("bwrite: bp %p blkno %lld offset %lld bcount %ld\n", bp, (long long)bp->b_blkno, (long long) offset, bp->b_bcount); if (lseek(bp->b_fd, offset, SEEK_SET) == -1) return (errno); rv = write(bp->b_fd, bp->b_data, bp->b_bcount); if (debug & DEBUG_BUF_BWRITE) printf("bwrite: write %ld (offset %lld) returned %lld\n", bp->b_bcount, (long long)offset, (long long)rv); if (rv == bp->b_bcount) return (0); else if (rv == -1) /* write error */ return (errno); else /* short write ? */ return (EAGAIN); } void bcleanup(void) { struct buf *bp; /* * XXX this really shouldn't be necessary, but i'm curious to * know why there's still some buffers lying around that * aren't brelse()d */ if (TAILQ_EMPTY(&buftail)) return; printf("bcleanup: unflushed buffers:\n"); TAILQ_FOREACH(bp, &buftail, b_tailq) { printf("\tlblkno %10lld blkno %10lld count %6ld bufsize %6ld\n", (long long)bp->b_lblkno, (long long)bp->b_blkno, bp->b_bcount, bp->b_bufsize); } printf("bcleanup: done\n"); } struct buf * getblk(int fd, struct fs *fs, daddr_t blkno, int size) { static int buftailinitted; struct buf *bp; void *n; assert (fs != NULL); if (debug & DEBUG_BUF_GETBLK) printf("getblk: fs %p blkno %lld size %d\n", fs, (long long)blkno, size); bp = NULL; if (!buftailinitted) { if (debug & DEBUG_BUF_GETBLK) printf("getblk: initialising tailq\n"); TAILQ_INIT(&buftail); buftailinitted = 1; } else { TAILQ_FOREACH(bp, &buftail, b_tailq) { if (bp->b_lblkno != blkno) continue; break; } } if (bp == NULL) { if ((bp = calloc(1, sizeof(struct buf))) == NULL) err(1, "getblk: calloc"); bp->b_bufsize = 0; bp->b_blkno = bp->b_lblkno = blkno; bp->b_fd = fd; bp->b_fs = fs; bp->b_data = NULL; TAILQ_INSERT_HEAD(&buftail, bp, b_tailq); } bp->b_bcount = size; if (bp->b_data == NULL || bp->b_bcount > bp->b_bufsize) { n = realloc(bp->b_data, size); if (n == NULL) err(1, "getblk: realloc b_data %ld", bp->b_bcount); bp->b_data = n; bp->b_bufsize = size; } return (bp); } makefs/src/usr.sbin/makefs/ffs/buf.h010064400000000000000000000045721045275301400145670ustar00/* $NetBSD: buf.h,v 1.2 2001/11/02 03:12:49 lukem Exp $ */ /* * Copyright (c) 2001 Wasabi Systems, Inc. * All rights reserved. * * Written by Luke Mewburn 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 _FFS_BUF_H #define _FFS_BUF_H #include #include struct buf { void * b_data; long b_bufsize; long b_bcount; daddr_t b_blkno; daddr_t b_lblkno; int b_fd; struct fs * b_fs; TAILQ_ENTRY(buf) b_tailq; }; void bcleanup(void); int bread(int, struct fs *, daddr_t, int, struct buf **); void brelse(struct buf *); int bwrite(struct buf *); struct buf * getblk(int, struct fs *, daddr_t, int); #define bdwrite(bp) bwrite(bp) #define clrbuf(bp) memset((bp)->b_data, 0, (u_int)(bp)->b_bcount) #endif /* _FFS_BUF_H */ makefs/src/usr.sbin/makefs/ffs/ffs_alloc.c010064400000000000000000000476741314214553200157470ustar00/* $NetBSD: ffs_alloc.c,v 1.17 2006/12/18 21:03:29 christos Exp $ */ /* From: NetBSD: ffs_alloc.c,v 1.50 2001/09/06 02:16:01 lukem Exp */ /* * 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) 1982, 1986, 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. * * @(#)ffs_alloc.c 8.19 (Berkeley) 7/13/95 */ #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #include #if defined(__RCSID) && !defined(__lint) __RCSID("$NetBSD: ffs_alloc.c,v 1.17 2006/12/18 21:03:29 christos Exp $"); __IDSTRING(mbsdid, "$MirOS: src/usr.sbin/makefs/ffs/ffs_alloc.c,v 1.7 2017/08/07 20:19:11 tg Exp $"); #endif /* !__lint */ #include #include #include #include "makefs.h" #include #include #include #include "ffs/buf.h" #include "ffs/ufs_inode.h" #include "ffs/ffs_extern.h" static int scanc(u_int, const u_char *, const u_char *, int); static daddr_t ffs_alloccg(struct inode *, int, daddr_t, int); static daddr_t ffs_alloccgblk(struct inode *, struct buf *, daddr_t); static daddr_t ffs_hashalloc(struct inode *, int, daddr_t, int, daddr_t (*)(struct inode *, int, daddr_t, int)); static int32_t ffs_mapsearch(struct fs *, struct cg *, daddr_t, int); /* in ffs_tables.c */ extern const int inside[], around[]; extern const u_char * const fragtbl[]; /* * Allocate a block in the filesystem. * * The size of the requested block is given, which must be some * multiple of fs_fsize and <= fs_bsize. * A preference may be optionally specified. If a preference is given * the following hierarchy is used to allocate a block: * 1) allocate the requested block. * 2) allocate a rotationally optimal block in the same cylinder. * 3) allocate a block in the same cylinder group. * 4) quadradically rehash into other cylinder groups, until an * available block is located. * If no block preference is given the following hierarchy is used * to allocate a block: * 1) allocate a block in the cylinder group that contains the * inode for the file. * 2) quadradically rehash into other cylinder groups, until an * available block is located. */ int ffs_alloc(struct inode *ip, daddr_t lbn __unused, daddr_t bpref, int size, daddr_t *bnp) { struct fs *fs = ip->i_fs; daddr_t bno; int cg; *bnp = 0; if (size > fs->fs_bsize || fragoff(fs, size) != 0) { errx(1, "ffs_alloc: bad size: bsize %d size %d", fs->fs_bsize, size); } if (size == fs->fs_bsize && fs->fs_cstotal.cs_nbfree == 0) goto nospace; if (bpref >= fs->fs_size) bpref = 0; if (bpref == 0) cg = ino_to_cg(fs, ip->i_number); else cg = dtog(fs, bpref); bno = ffs_hashalloc(ip, cg, bpref, size, ffs_alloccg); if (bno > 0) { DIP_ADD(ip, blocks, size / 512); *bnp = bno; return (0); } nospace: return (ENOSPC); } /* * Select the desired position for the next block in a file. The file is * logically divided into sections. The first section is composed of the * direct blocks. Each additional section contains fs_maxbpg blocks. * * If no blocks have been allocated in the first section, the policy is to * request a block in the same cylinder group as the inode that describes * the file. If no blocks have been allocated in any other section, the * policy is to place the section in a cylinder group with a greater than * average number of free blocks. An appropriate cylinder group is found * by using a rotor that sweeps the cylinder groups. When a new group of * blocks is needed, the sweep begins in the cylinder group following the * cylinder group from which the previous allocation was made. The sweep * continues until a cylinder group with greater than the average number * of free blocks is found. If the allocation is for the first block in an * indirect block, the information on the previous allocation is unavailable; * here a best guess is made based upon the logical block number being * allocated. * * If a section is already partially allocated, the policy is to * contiguously allocate fs_maxcontig blocks. The end of one of these * contiguous blocks and the beginning of the next is physically separated * so that the disk head will be in transit between them for at least * fs_rotdelay milliseconds. This is to allow time for the processor to * schedule another I/O transfer. */ /* XXX ondisk32 */ daddr_t ffs_blkpref_ufs1(struct inode *ip, daddr_t lbn, int indx, int32_t *bap) { struct fs *fs; int cg; int avgbfree, startcg; fs = ip->i_fs; if (indx % fs->fs_maxbpg == 0 || bap[indx - 1] == 0) { if (lbn < NDADDR + NINDIR(fs)) { cg = ino_to_cg(fs, ip->i_number); return (fs->fs_fpg * cg + fs->fs_frag); } /* * Find a cylinder with greater than average number of * unused data blocks. */ if (indx == 0 || bap[indx - 1] == 0) startcg = ino_to_cg(fs, ip->i_number) + lbn / fs->fs_maxbpg; else startcg = dtog(fs, ufs_rw32(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + 1); startcg %= fs->fs_ncg; avgbfree = fs->fs_cstotal.cs_nbfree / fs->fs_ncg; for (cg = startcg; cg < fs->fs_ncg; cg++) if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) return (fs->fs_fpg * cg + fs->fs_frag); for (cg = 0; cg <= startcg; cg++) if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) return (fs->fs_fpg * cg + fs->fs_frag); return (0); } /* * We just always try to lay things out contiguously. */ return ufs_rw32(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + fs->fs_frag; } daddr_t ffs_blkpref_ufs2(struct inode *ip, daddr_t lbn, int indx, int64_t *bap) { struct fs *fs; int cg; int avgbfree, startcg; fs = ip->i_fs; if (indx % fs->fs_maxbpg == 0 || bap[indx - 1] == 0) { if (lbn < NDADDR + NINDIR(fs)) { cg = ino_to_cg(fs, ip->i_number); return (fs->fs_fpg * cg + fs->fs_frag); } /* * Find a cylinder with greater than average number of * unused data blocks. */ if (indx == 0 || bap[indx - 1] == 0) startcg = ino_to_cg(fs, ip->i_number) + lbn / fs->fs_maxbpg; else startcg = dtog(fs, ufs_rw64(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + 1); startcg %= fs->fs_ncg; avgbfree = fs->fs_cstotal.cs_nbfree / fs->fs_ncg; for (cg = startcg; cg < fs->fs_ncg; cg++) if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) { return (fs->fs_fpg * cg + fs->fs_frag); } for (cg = 0; cg < startcg; cg++) if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) { return (fs->fs_fpg * cg + fs->fs_frag); } return (0); } /* * We just always try to lay things out contiguously. */ return ufs_rw64(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + fs->fs_frag; } /* * Implement the cylinder overflow algorithm. * * The policy implemented by this algorithm is: * 1) allocate the block in its requested cylinder group. * 2) quadradically rehash on the cylinder group number. * 3) brute force search for a free block. * * `size': size for data blocks, mode for inodes */ /*VARARGS5*/ static daddr_t ffs_hashalloc(struct inode *ip, int cg, daddr_t pref, int size, daddr_t (*allocator)(struct inode *, int, daddr_t, int)) { struct fs *fs; daddr_t result; int i, icg = cg; fs = ip->i_fs; /* * 1: preferred cylinder group */ result = (*allocator)(ip, cg, pref, size); if (result) return (result); /* * 2: quadratic rehash */ for (i = 1; i < fs->fs_ncg; i *= 2) { cg += i; if (cg >= fs->fs_ncg) cg -= fs->fs_ncg; result = (*allocator)(ip, cg, 0, size); if (result) return (result); } /* * 3: brute force search * Note that we start at i == 2, since 0 was checked initially, * and 1 is always checked in the quadratic rehash. */ cg = (icg + 2) % fs->fs_ncg; for (i = 2; i < fs->fs_ncg; i++) { result = (*allocator)(ip, cg, 0, size); if (result) return (result); cg++; if (cg == fs->fs_ncg) cg = 0; } return (0); } /* * Determine whether a block can be allocated. * * Check to see if a block of the appropriate size is available, * and if it is, allocate it. */ static daddr_t ffs_alloccg(struct inode *ip, int cg, daddr_t bpref, int size) { struct cg *cgp; struct buf *bp; daddr_t bno, blkno; int error, frags, allocsiz, i; struct fs *fs = ip->i_fs; const int needswap = UFS_FSNEEDSWAP(fs); if (fs->fs_cs(fs, cg).cs_nbfree == 0 && size == fs->fs_bsize) return (0); error = bread(ip->i_fd, ip->i_fs, fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &bp); if (error) { brelse(bp); return (0); } cgp = (struct cg *)bp->b_data; if (!cg_chkmagic(cgp, needswap) || (cgp->cg_cs.cs_nbfree == 0 && size == fs->fs_bsize)) { brelse(bp); return (0); } if (size == fs->fs_bsize) { bno = ffs_alloccgblk(ip, bp, bpref); bdwrite(bp); return (bno); } /* * check to see if any fragments are already available * allocsiz is the size which will be allocated, hacking * it down to a smaller size if necessary */ frags = numfrags(fs, size); for (allocsiz = frags; allocsiz < fs->fs_frag; allocsiz++) if (cgp->cg_frsum[allocsiz] != 0) break; if (allocsiz == fs->fs_frag) { /* * no fragments were available, so a block will be * allocated, and hacked up */ if (cgp->cg_cs.cs_nbfree == 0) { brelse(bp); return (0); } bno = ffs_alloccgblk(ip, bp, bpref); bpref = dtogd(fs, bno); for (i = frags; i < fs->fs_frag; i++) setbit(cg_blksfree(cgp, needswap), bpref + i); i = fs->fs_frag - frags; ufs_add32(cgp->cg_cs.cs_nffree, i, needswap); fs->fs_cstotal.cs_nffree += i; fs->fs_cs(fs, cg).cs_nffree += i; fs->fs_fmod = 1; ufs_add32(cgp->cg_frsum[i], 1, needswap); bdwrite(bp); return (bno); } bno = ffs_mapsearch(fs, cgp, bpref, allocsiz); for (i = 0; i < frags; i++) clrbit(cg_blksfree(cgp, needswap), bno + i); ufs_add32(cgp->cg_cs.cs_nffree, -frags, needswap); fs->fs_cstotal.cs_nffree -= frags; fs->fs_cs(fs, cg).cs_nffree -= frags; fs->fs_fmod = 1; ufs_add32(cgp->cg_frsum[allocsiz], -1, needswap); if (frags != allocsiz) ufs_add32(cgp->cg_frsum[allocsiz - frags], 1, needswap); blkno = cg * fs->fs_fpg + bno; bdwrite(bp); return blkno; } /* * Allocate a block in a cylinder group. * * This algorithm implements the following policy: * 1) allocate the requested block. * 2) allocate a rotationally optimal block in the same cylinder. * 3) allocate the next available block on the block rotor for the * specified cylinder group. * Note that this routine only allocates fs_bsize blocks; these * blocks may be fragmented by the routine that allocates them. */ static daddr_t ffs_alloccgblk(struct inode *ip, struct buf *bp, daddr_t bpref) { struct cg *cgp; daddr_t blkno; int32_t bno; struct fs *fs = ip->i_fs; const int needswap = UFS_FSNEEDSWAP(fs); u_int8_t *blksfree; cgp = (struct cg *)bp->b_data; blksfree = cg_blksfree(cgp, needswap); if (bpref == 0 || (uint32_t)dtog(fs, bpref) != ufs_rw32(cgp->cg_cgx, needswap)) { bpref = ufs_rw32(cgp->cg_rotor, needswap); } else { bpref = blknum(fs, bpref); bno = dtogd(fs, bpref); /* * if the requested block is available, use it */ if (ffs_isblock(fs, blksfree, fragstoblks(fs, bno))) goto gotit; } /* * Take the next available one in this cylinder group. */ bno = ffs_mapsearch(fs, cgp, bpref, (int)fs->fs_frag); if (bno < 0) return (0); cgp->cg_rotor = ufs_rw32(bno, needswap); gotit: blkno = fragstoblks(fs, bno); ffs_clrblock(fs, blksfree, (long)blkno); ffs_clusteracct(fs, cgp, blkno, -1); ufs_add32(cgp->cg_cs.cs_nbfree, -1, needswap); fs->fs_cstotal.cs_nbfree--; fs->fs_cs(fs, ufs_rw32(cgp->cg_cgx, needswap)).cs_nbfree--; fs->fs_fmod = 1; blkno = ufs_rw32(cgp->cg_cgx, needswap) * fs->fs_fpg + bno; return (blkno); } /* * Free a block or fragment. * * The specified block or fragment is placed back in the * free map. If a fragment is deallocated, a possible * block reassembly is checked. */ void ffs_blkfree(struct inode *ip, daddr_t bno, long size) { struct cg *cgp; struct buf *bp; int32_t fragno, cgbno; int i, error, cg, blk, frags, bbase; struct fs *fs = ip->i_fs; const int needswap = UFS_FSNEEDSWAP(fs); if (size > fs->fs_bsize || fragoff(fs, size) != 0 || fragnum(fs, bno) + numfrags(fs, size) > fs->fs_frag) { errx(1, "blkfree: bad size: bno %lld bsize %d size %ld", (long long)bno, fs->fs_bsize, size); } cg = dtog(fs, bno); if (bno >= fs->fs_size) { warnx("bad block %lld, ino %llu", (long long)bno, (unsigned long long)ip->i_number); return; } error = bread(ip->i_fd, ip->i_fs, fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &bp); if (error) { brelse(bp); return; } cgp = (struct cg *)bp->b_data; if (!cg_chkmagic(cgp, needswap)) { brelse(bp); return; } cgbno = dtogd(fs, bno); if (size == fs->fs_bsize) { fragno = fragstoblks(fs, cgbno); if (!ffs_isfreeblock(fs, cg_blksfree(cgp, needswap), fragno)) { errx(1, "blkfree: freeing free block %lld", (long long)bno); } ffs_setblock(fs, cg_blksfree(cgp, needswap), fragno); ffs_clusteracct(fs, cgp, fragno, 1); ufs_add32(cgp->cg_cs.cs_nbfree, 1, needswap); fs->fs_cstotal.cs_nbfree++; fs->fs_cs(fs, cg).cs_nbfree++; } else { bbase = cgbno - fragnum(fs, cgbno); /* * decrement the counts associated with the old frags */ blk = blkmap(fs, cg_blksfree(cgp, needswap), bbase); ffs_fragacct(fs, blk, cgp->cg_frsum, -1, needswap); /* * deallocate the fragment */ frags = numfrags(fs, size); for (i = 0; i < frags; i++) { if (isset(cg_blksfree(cgp, needswap), cgbno + i)) { errx(1, "blkfree: freeing free frag: block %lld", (long long)(cgbno + i)); } setbit(cg_blksfree(cgp, needswap), cgbno + i); } ufs_add32(cgp->cg_cs.cs_nffree, i, needswap); fs->fs_cstotal.cs_nffree += i; fs->fs_cs(fs, cg).cs_nffree += i; /* * add back in counts associated with the new frags */ blk = blkmap(fs, cg_blksfree(cgp, needswap), bbase); ffs_fragacct(fs, blk, cgp->cg_frsum, 1, needswap); /* * if a complete block has been reassembled, account for it */ fragno = fragstoblks(fs, bbase); if (ffs_isblock(fs, cg_blksfree(cgp, needswap), fragno)) { ufs_add32(cgp->cg_cs.cs_nffree, -fs->fs_frag, needswap); fs->fs_cstotal.cs_nffree -= fs->fs_frag; fs->fs_cs(fs, cg).cs_nffree -= fs->fs_frag; ffs_clusteracct(fs, cgp, fragno, 1); ufs_add32(cgp->cg_cs.cs_nbfree, 1, needswap); fs->fs_cstotal.cs_nbfree++; fs->fs_cs(fs, cg).cs_nbfree++; } } fs->fs_fmod = 1; bdwrite(bp); } static int scanc(u_int size, const u_char *cp, const u_char table[], int mask) { const u_char *end = &cp[size]; while (cp < end && (table[*cp] & mask) == 0) cp++; return (end - cp); } /* * Find a block of the specified size in the specified cylinder group. * * It is a panic if a request is made to find a block if none are * available. */ static int32_t ffs_mapsearch(struct fs *fs, struct cg *cgp, daddr_t bpref, int allocsiz) { int32_t bno; int start, len, loc, i; int blk, field, subfield, pos; int ostart, olen; const int needswap = UFS_FSNEEDSWAP(fs); /* * find the fragment by searching through the free block * map for an appropriate bit pattern */ if (bpref) start = dtogd(fs, bpref) / NBBY; else start = ufs_rw32(cgp->cg_frotor, needswap) / NBBY; len = howmany(fs->fs_fpg, NBBY) - start; ostart = start; olen = len; loc = scanc((u_int)len, (const u_char *)&cg_blksfree(cgp, needswap)[start], (const u_char *)fragtbl[fs->fs_frag], (1 << (allocsiz - 1 + (fs->fs_frag % NBBY)))); if (loc == 0) { len = start + 1; start = 0; loc = scanc((u_int)len, (const u_char *)&cg_blksfree(cgp, needswap)[0], (const u_char *)fragtbl[fs->fs_frag], (1 << (allocsiz - 1 + (fs->fs_frag % NBBY)))); if (loc == 0) { errx(1, "ffs_alloccg: map corrupted: start %d len %d offset %d %ld", ostart, olen, ufs_rw32(cgp->cg_freeoff, needswap), (long)cg_blksfree(cgp, needswap) - (long)cgp); /* NOTREACHED */ } } bno = (start + len - loc) * NBBY; cgp->cg_frotor = ufs_rw32(bno, needswap); /* * found the byte in the map * sift through the bits to find the selected frag */ for (i = bno + NBBY; bno < i; bno += fs->fs_frag) { blk = blkmap(fs, cg_blksfree(cgp, needswap), bno); blk <<= 1; field = around[allocsiz]; subfield = inside[allocsiz]; for (pos = 0; pos <= fs->fs_frag - allocsiz; pos++) { if ((blk & field) == subfield) return (bno + pos); field <<= 1; subfield <<= 1; } } errx(1, "ffs_alloccg: block not in map: bno %lld", (long long)bno); return (-1); } /* * Update the cluster map because of an allocation or free. * * Cnt == 1 means free; cnt == -1 means allocating. */ void ffs_clusteracct(struct fs *fs, struct cg *cgp, int32_t blkno, int cnt) { int32_t *sump; int32_t *lp; u_char *freemapp, *mapp; int i, start, end, forw, back, map, bit; const int needswap = UFS_FSNEEDSWAP(fs); if (fs->fs_contigsumsize <= 0) return; freemapp = cg_clustersfree(cgp, needswap); sump = cg_clustersum(cgp, needswap); /* * Allocate or clear the actual block. */ if (cnt > 0) setbit(freemapp, blkno); else clrbit(freemapp, blkno); /* * Find the size of the cluster going forward. */ start = blkno + 1; end = start + fs->fs_contigsumsize; if ((unsigned)end >= ufs_rw32(cgp->cg_nclusterblks, needswap)) end = ufs_rw32(cgp->cg_nclusterblks, needswap); mapp = &freemapp[start / NBBY]; map = *mapp++; bit = 1 << (start % NBBY); for (i = start; i < end; i++) { if ((map & bit) == 0) break; if ((i & (NBBY - 1)) != (NBBY - 1)) { bit <<= 1; } else { map = *mapp++; bit = 1; } } forw = i - start; /* * Find the size of the cluster going backward. */ start = blkno - 1; end = start - fs->fs_contigsumsize; if (end < 0) end = -1; mapp = &freemapp[start / NBBY]; map = *mapp--; bit = 1 << (start % NBBY); for (i = start; i > end; i--) { if ((map & bit) == 0) break; if ((i & (NBBY - 1)) != 0) { bit >>= 1; } else { map = *mapp--; bit = 1 << (NBBY - 1); } } back = start - i; /* * Account for old cluster and the possibly new forward and * back clusters. */ i = back + forw + 1; if (i > fs->fs_contigsumsize) i = fs->fs_contigsumsize; ufs_add32(sump[i], cnt, needswap); if (back > 0) ufs_add32(sump[back], -cnt, needswap); if (forw > 0) ufs_add32(sump[forw], -cnt, needswap); /* * Update cluster summary information. */ lp = &sump[fs->fs_contigsumsize]; for (i = fs->fs_contigsumsize; i > 0; i--) if (ufs_rw32(*lp--, needswap) > 0) break; fs->fs_maxcluster[ufs_rw32(cgp->cg_cgx, needswap)] = i; } makefs/src/usr.sbin/makefs/ffs/ffs_balloc.c010064400000000000000000000331501314214553200160710ustar00/* $NetBSD: ffs_balloc.c,v 1.13 2004/06/20 22:20:18 jmc Exp $ */ /* From NetBSD: ffs_balloc.c,v 1.25 2001/08/08 08:36:36 lukem Exp */ /* * Copyright (c) 1982, 1986, 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. * * @(#)ffs_balloc.c 8.8 (Berkeley) 6/16/95 */ #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #include #if defined(__RCSID) && !defined(__lint) __RCSID("$NetBSD: ffs_balloc.c,v 1.13 2004/06/20 22:20:18 jmc Exp $"); __IDSTRING(mbsdid, "$MirOS: src/usr.sbin/makefs/ffs/ffs_balloc.c,v 1.6 2017/08/07 20:19:11 tg Exp $"); #endif /* !__lint */ #include #include #include #include #include #include #include #include "makefs.h" #include #include #include #include "ffs/buf.h" #include "ffs/ufs_inode.h" #include "ffs/ffs_extern.h" static int ffs_balloc_ufs1(struct inode *, off_t, int, struct buf **); static int ffs_balloc_ufs2(struct inode *, off_t, int, struct buf **); /* * Balloc defines the structure of filesystem storage * by allocating the physical blocks on a device given * the inode and the logical block number in a file. * * Assume: flags == B_SYNC | B_CLRBUF */ int ffs_balloc(struct inode *ip, off_t offset, int bufsize, struct buf **bpp) { if (ip->i_fs->fs_magic == FS_UFS2_MAGIC) return ffs_balloc_ufs2(ip, offset, bufsize, bpp); else return ffs_balloc_ufs1(ip, offset, bufsize, bpp); } static int ffs_balloc_ufs1(struct inode *ip, off_t offset, int bufsize, struct buf **bpp) { daddr_t lbn, lastlbn; int size; int32_t nb; struct buf *bp, *nbp; struct fs *fs = ip->i_fs; struct indir indirs[NIADDR + 2]; daddr_t newb, pref; int32_t *bap; int osize, nsize, num, i, error; int32_t *allocblk, allociblk[NIADDR + 1]; int32_t *allocib; const int needswap = UFS_FSNEEDSWAP(fs); lbn = lblkno(fs, offset); size = blkoff(fs, offset) + bufsize; if (bpp != NULL) { *bpp = NULL; } assert(size <= fs->fs_bsize); if (lbn < 0) return (EFBIG); /* * If the next write will extend the file into a new block, * and the file is currently composed of a fragment * this fragment has to be extended to be a full block. */ lastlbn = lblkno(fs, ip->i_ffs1_size); if (lastlbn < NDADDR && lastlbn < lbn) { nb = lastlbn; osize = blksize(fs, ip, nb); if (osize < fs->fs_bsize && osize > 0) { warnx("need to ffs_realloccg; not supported!"); abort(); } } /* * The first NDADDR blocks are direct blocks */ if (lbn < NDADDR) { nb = ufs_rw32(ip->i_ffs1_db[lbn], needswap); if (nb != 0 && (off_t)ip->i_ffs1_size >= lblktosize(fs, lbn + 1)) { /* * The block is an already-allocated direct block * and the file already extends past this block, * thus this must be a whole block. * Just read the block (if requested). */ if (bpp != NULL) { error = bread(ip->i_fd, ip->i_fs, lbn, fs->fs_bsize, bpp); if (error) { brelse(*bpp); return (error); } } return (0); } if (nb != 0) { /* * Consider need to reallocate a fragment. */ osize = fragroundup(fs, blkoff(fs, ip->i_ffs1_size)); nsize = fragroundup(fs, size); if (nsize <= osize) { /* * The existing block is already * at least as big as we want. * Just read the block (if requested). */ if (bpp != NULL) { error = bread(ip->i_fd, ip->i_fs, lbn, osize, bpp); if (error) { brelse(*bpp); return (error); } } return 0; } else { warnx("need to ffs_realloccg; not supported!"); abort(); } } else { /* * the block was not previously allocated, * allocate a new block or fragment. */ if ((off_t)ip->i_ffs1_size < lblktosize(fs, lbn + 1)) nsize = fragroundup(fs, size); else nsize = fs->fs_bsize; error = ffs_alloc(ip, lbn, ffs_blkpref_ufs1(ip, lbn, (int)lbn, &ip->i_ffs1_db[0]), nsize, &newb); if (error) return (error); if (bpp != NULL) { bp = getblk(ip->i_fd, ip->i_fs, lbn, nsize); bp->b_blkno = fsbtodb(fs, newb); clrbuf(bp); *bpp = bp; } } ip->i_ffs1_db[lbn] = ufs_rw32((int32_t)newb, needswap); return (0); } /* * Determine the number of levels of indirection. */ pref = 0; if ((error = ufs_getlbns(ip, lbn, indirs, &num)) != 0) return (error); if (num < 1) { warnx("ffs_balloc: ufs_getlbns returned indirect block"); abort(); } /* * Fetch the first indirect block allocating if necessary. */ --num; nb = ufs_rw32(ip->i_ffs1_ib[indirs[0].in_off], needswap); allocib = NULL; allocblk = allociblk; if (nb == 0) { pref = ffs_blkpref_ufs1(ip, lbn, 0, (int32_t *)0); error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb); if (error) return error; nb = newb; *allocblk++ = nb; bp = getblk(ip->i_fd, ip->i_fs, indirs[1].in_lbn, fs->fs_bsize); bp->b_blkno = fsbtodb(fs, nb); clrbuf(bp); /* * Write synchronously so that indirect blocks * never point at garbage. */ if ((error = bwrite(bp)) != 0) return error; allocib = &ip->i_ffs1_ib[indirs[0].in_off]; *allocib = ufs_rw32((int32_t)nb, needswap); } /* * Fetch through the indirect blocks, allocating as necessary. */ for (i = 1;;) { error = bread(ip->i_fd, ip->i_fs, indirs[i].in_lbn, fs->fs_bsize, &bp); if (error) { brelse(bp); return error; } bap = (int32_t *)bp->b_data; nb = ufs_rw32(bap[indirs[i].in_off], needswap); if (i == num) break; i++; if (nb != 0) { brelse(bp); continue; } if (pref == 0) pref = ffs_blkpref_ufs1(ip, lbn, 0, (int32_t *)0); error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb); if (error) { brelse(bp); return error; } nb = newb; *allocblk++ = nb; nbp = getblk(ip->i_fd, ip->i_fs, indirs[i].in_lbn, fs->fs_bsize); nbp->b_blkno = fsbtodb(fs, nb); clrbuf(nbp); /* * Write synchronously so that indirect blocks * never point at garbage. */ if ((error = bwrite(nbp)) != 0) { brelse(bp); return error; } bap[indirs[i - 1].in_off] = ufs_rw32(nb, needswap); bwrite(bp); } /* * Get the data block, allocating if necessary. */ if (nb == 0) { pref = ffs_blkpref_ufs1(ip, lbn, indirs[num].in_off, &bap[0]); error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb); if (error) { brelse(bp); return error; } nb = newb; *allocblk++ = nb; if (bpp != NULL) { nbp = getblk(ip->i_fd, ip->i_fs, lbn, fs->fs_bsize); nbp->b_blkno = fsbtodb(fs, nb); clrbuf(nbp); *bpp = nbp; } bap[indirs[num].in_off] = ufs_rw32(nb, needswap); /* * If required, write synchronously, otherwise use * delayed write. */ bwrite(bp); return (0); } brelse(bp); if (bpp != NULL) { error = bread(ip->i_fd, ip->i_fs, lbn, (int)fs->fs_bsize, &nbp); if (error) { brelse(nbp); return error; } *bpp = nbp; } return (0); } static int ffs_balloc_ufs2(struct inode *ip, off_t offset, int bufsize, struct buf **bpp) { daddr_t lbn, lastlbn; int size; struct buf *bp, *nbp; struct fs *fs = ip->i_fs; struct indir indirs[NIADDR + 2]; daddr_t newb, pref, nb; int64_t *bap; int osize, nsize, num, i, error; int64_t *allocblk, allociblk[NIADDR + 1]; int64_t *allocib; const int needswap = UFS_FSNEEDSWAP(fs); lbn = lblkno(fs, offset); size = blkoff(fs, offset) + bufsize; if (bpp != NULL) { *bpp = NULL; } assert(size <= fs->fs_bsize); if (lbn < 0) return (EFBIG); /* * If the next write will extend the file into a new block, * and the file is currently composed of a fragment * this fragment has to be extended to be a full block. */ lastlbn = lblkno(fs, ip->i_ffs2_size); if (lastlbn < NDADDR && lastlbn < lbn) { nb = lastlbn; osize = blksize(fs, ip, nb); if (osize < fs->fs_bsize && osize > 0) { warnx("need to ffs_realloccg; not supported!"); abort(); } } /* * The first NDADDR blocks are direct blocks */ if (lbn < NDADDR) { nb = ufs_rw64(ip->i_ffs2_db[lbn], needswap); if (nb != 0 && (off_t)ip->i_ffs2_size >= lblktosize(fs, lbn + 1)) { /* * The block is an already-allocated direct block * and the file already extends past this block, * thus this must be a whole block. * Just read the block (if requested). */ if (bpp != NULL) { error = bread(ip->i_fd, ip->i_fs, lbn, fs->fs_bsize, bpp); if (error) { brelse(*bpp); return (error); } } return (0); } if (nb != 0) { /* * Consider need to reallocate a fragment. */ osize = fragroundup(fs, blkoff(fs, ip->i_ffs2_size)); nsize = fragroundup(fs, size); if (nsize <= osize) { /* * The existing block is already * at least as big as we want. * Just read the block (if requested). */ if (bpp != NULL) { error = bread(ip->i_fd, ip->i_fs, lbn, osize, bpp); if (error) { brelse(*bpp); return (error); } } return 0; } else { warnx("need to ffs_realloccg; not supported!"); abort(); } } else { /* * the block was not previously allocated, * allocate a new block or fragment. */ if ((off_t)ip->i_ffs2_size < lblktosize(fs, lbn + 1)) nsize = fragroundup(fs, size); else nsize = fs->fs_bsize; error = ffs_alloc(ip, lbn, ffs_blkpref_ufs2(ip, lbn, (int)lbn, &ip->i_ffs2_db[0]), nsize, &newb); if (error) return (error); if (bpp != NULL) { bp = getblk(ip->i_fd, ip->i_fs, lbn, nsize); bp->b_blkno = fsbtodb(fs, newb); clrbuf(bp); *bpp = bp; } } ip->i_ffs2_db[lbn] = ufs_rw64(newb, needswap); return (0); } /* * Determine the number of levels of indirection. */ pref = 0; if ((error = ufs_getlbns(ip, lbn, indirs, &num)) != 0) return (error); if (num < 1) { warnx("ffs_balloc: ufs_getlbns returned indirect block"); abort(); } /* * Fetch the first indirect block allocating if necessary. */ --num; nb = ufs_rw64(ip->i_ffs2_ib[indirs[0].in_off], needswap); allocib = NULL; allocblk = allociblk; if (nb == 0) { pref = ffs_blkpref_ufs2(ip, lbn, 0, (int64_t *)0); error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb); if (error) return error; nb = newb; *allocblk++ = nb; bp = getblk(ip->i_fd, ip->i_fs, indirs[1].in_lbn, fs->fs_bsize); bp->b_blkno = fsbtodb(fs, nb); clrbuf(bp); /* * Write synchronously so that indirect blocks * never point at garbage. */ if ((error = bwrite(bp)) != 0) return error; allocib = &ip->i_ffs2_ib[indirs[0].in_off]; *allocib = ufs_rw64(nb, needswap); } /* * Fetch through the indirect blocks, allocating as necessary. */ for (i = 1;;) { error = bread(ip->i_fd, ip->i_fs, indirs[i].in_lbn, fs->fs_bsize, &bp); if (error) { brelse(bp); return error; } bap = (int64_t *)bp->b_data; nb = ufs_rw64(bap[indirs[i].in_off], needswap); if (i == num) break; i++; if (nb != 0) { brelse(bp); continue; } if (pref == 0) pref = ffs_blkpref_ufs2(ip, lbn, 0, (int64_t *)0); error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb); if (error) { brelse(bp); return error; } nb = newb; *allocblk++ = nb; nbp = getblk(ip->i_fd, ip->i_fs, indirs[i].in_lbn, fs->fs_bsize); nbp->b_blkno = fsbtodb(fs, nb); clrbuf(nbp); /* * Write synchronously so that indirect blocks * never point at garbage. */ if ((error = bwrite(nbp)) != 0) { brelse(bp); return error; } bap[indirs[i - 1].in_off] = ufs_rw64(nb, needswap); bwrite(bp); } /* * Get the data block, allocating if necessary. */ if (nb == 0) { pref = ffs_blkpref_ufs2(ip, lbn, indirs[num].in_off, &bap[0]); error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb); if (error) { brelse(bp); return error; } nb = newb; *allocblk++ = nb; if (bpp != NULL) { nbp = getblk(ip->i_fd, ip->i_fs, lbn, fs->fs_bsize); nbp->b_blkno = fsbtodb(fs, nb); clrbuf(nbp); *bpp = nbp; } bap[indirs[num].in_off] = ufs_rw64(nb, needswap); /* * If required, write synchronously, otherwise use * delayed write. */ bwrite(bp); return (0); } brelse(bp); if (bpp != NULL) { error = bread(ip->i_fd, ip->i_fs, lbn, (int)fs->fs_bsize, &nbp); if (error) { brelse(nbp); return error; } *bpp = nbp; } return (0); } makefs/src/usr.sbin/makefs/ffs/ffs_extern.h010064400000000000000000000065521223453423000161530ustar00/* $MirOS: src/usr.sbin/makefs/ffs/ffs_extern.h,v 1.2 2013/10/31 20:07:27 tg Exp $ */ /* $NetBSD: ffs_extern.h,v 1.6 2003/08/07 11:25:33 agc Exp $ */ /* From: NetBSD: ffs_extern.h,v 1.19 2001/08/17 02:18:48 lukem Exp */ /*- * Copyright © 2013 * Thorsten “mirabilos” Glaser * Copyright (c) 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. * * @(#)ffs_extern.h 8.6 (Berkeley) 3/30/95 */ #include "ffs/buf.h" /* * Structure used to pass around logical block paths generated by * ufs_getlbns and used by truncate and bmap code. */ struct indir { daddr_t in_lbn; /* Logical block number. */ int in_off; /* Offset in buffer. */ int in_exists; /* Flag if the block exists. */ }; /* ffs.c */ void panic(const char *, ...) __attribute__((__noreturn__, __format__(__printf__, 1, 2))); /* ffs_alloc.c */ int ffs_alloc(struct inode *, daddr_t, daddr_t, int, daddr_t *); daddr_t ffs_blkpref_ufs1(struct inode *, daddr_t, int, int32_t *); daddr_t ffs_blkpref_ufs2(struct inode *, daddr_t, int, int64_t *); void ffs_blkfree(struct inode *, daddr_t, long); void ffs_clusteracct(struct fs *, struct cg *, int32_t, int); /* ffs_balloc.c */ int ffs_balloc(struct inode *, off_t, int, struct buf **); /* ffs_bswap.c */ void ffs_sb_swap(struct fs*, struct fs *); void ffs_dinode1_swap(struct ufs1_dinode *, struct ufs1_dinode *); void ffs_dinode2_swap(struct ufs2_dinode *, struct ufs2_dinode *); void ffs_csum_swap(struct csum *, struct csum *, int); void ffs_cg_swap(struct cg *, struct cg *, struct fs *); /* ffs_subr.c */ void ffs_fragacct(struct fs *, int, int32_t[], int, int); int ffs_isblock(struct fs *, u_char *, int32_t); int ffs_isfreeblock(struct fs *, u_char *, int32_t); void ffs_clrblock(struct fs *, u_char *, int32_t); void ffs_setblock(struct fs *, u_char *, int32_t); /* ufs_bmap.c */ int ufs_getlbns(struct inode *, daddr_t, struct indir *, int *); makefs/src/usr.sbin/makefs/ffs/mkfs.c010064400000000000000000000645271314214553200147530ustar00/* $NetBSD: mkfs.c,v 1.21 2004/12/20 20:51:42 jmc Exp $ */ /* * 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) 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. */ #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #include #ifndef lint #if 0 static char sccsid[] = "@(#)mkfs.c 8.11 (Berkeley) 5/3/95"; #else #ifdef __RCSID __RCSID("$NetBSD: mkfs.c,v 1.21 2004/12/20 20:51:42 jmc Exp $"); __IDSTRING(mbsdid, "$MirOS: src/usr.sbin/makefs/ffs/mkfs.c,v 1.11 2017/08/07 20:19:11 tg Exp $"); #endif #endif #endif /* not lint */ #include #include #include #include #include #include #include #include #include "makefs.h" #include "ffs.h" #include #include #include #include "ffs/ufs_inode.h" #include "ffs/ffs_extern.h" #include "ffs/newfs_extern.h" static void initcg(int, time_t, const fsinfo_t *); static int ilog2(int); static int count_digits(int); #ifdef __MirBSD__ #define randomx arc4random #else #define randomx random #endif /* * make filesystem for cylinder-group style filesystems */ #define UMASK 0755 #define POWEROF2(num) (((num) & ((num) - 1)) == 0) union { struct fs fs; char pad[SBLOCKSIZE]; } fsun; #define sblock fsun.fs struct csum *fscs; union { struct cg cg; char pad[FFS_MAXBSIZE]; } cgun; #define acg cgun.cg char *iobuf; int iobufsize; char writebuf[FFS_MAXBSIZE]; static int Oflag; /* format as an 4.3BSD filesystem */ static int64_t fssize; /* filesystem size */ static int sectorsize; /* bytes/sector */ static int fsize; /* fragment size */ static int bsize; /* block size */ static int maxbsize; /* maximum clustering */ static int maxblkspercg; static int minfree; /* free space threshold */ static int opt; /* optimization preference (space or time) */ static int density; /* number of bytes per inode */ static int maxcontig; /* max contiguous blocks to allocate */ static int maxbpg; /* maximum blocks per file in a cyl group */ static int bbsize; /* boot block size */ static int sbsize; /* superblock size */ static int avgfilesize; /* expected average file size */ static int avgfpdir; /* expected number of files per directory */ struct fs * ffs_mkfs(const char *fsys, const fsinfo_t *fsopts) { int fragsperinode, optimalfpg, origdensity, minfpg, lastminfpg; int32_t cylno, i, csfrags; long long sizepb; void *space; int size, blks; int nprintcols, printcolwidth; ffs_opt_t *ffs_opts = fsopts->fs_specific; Oflag = ffs_opts->version; fssize = fsopts->size / fsopts->sectorsize; sectorsize = fsopts->sectorsize; fsize = ffs_opts->fsize; bsize = ffs_opts->bsize; maxbsize = ffs_opts->maxbsize; maxblkspercg = ffs_opts->maxblkspercg; minfree = ffs_opts->minfree; opt = ffs_opts->optimization; density = ffs_opts->density; maxcontig = ffs_opts->maxcontig; maxbpg = ffs_opts->maxbpg; avgfilesize = ffs_opts->avgfilesize; avgfpdir = ffs_opts->avgfpdir; bbsize = BBSIZE; sbsize = SBLOCKSIZE; if (Oflag == 0) { sblock.fs_old_inodefmt = FS_42INODEFMT; sblock.fs_maxsymlinklen = 0; sblock.fs_old_flags = 0; } else { sblock.fs_old_inodefmt = FS_44INODEFMT; sblock.fs_maxsymlinklen = (Oflag == 1 ? MAXSYMLINKLEN_UFS1 : MAXSYMLINKLEN_UFS2); sblock.fs_old_flags = FS_FLAGS_UPDATED; sblock.fs_flags = 0; } /* * Validate the given filesystem size. * Verify that its last block can actually be accessed. * Convert to filesystem fragment sized units. */ if (fssize <= 0) { printf("preposterous size %lld\n", (long long)fssize); exit(13); } ffs_wtfs(fssize - 1, sectorsize, (char *)&sblock, fsopts); /* * collect and verify the filesystem density info */ sblock.fs_avgfilesize = avgfilesize; sblock.fs_avgfpdir = avgfpdir; if (sblock.fs_avgfilesize <= 0) printf("illegal expected average file size %d\n", sblock.fs_avgfilesize), exit(14); if (sblock.fs_avgfpdir <= 0) printf("illegal expected number of files per directory %d\n", sblock.fs_avgfpdir), exit(15); /* * collect and verify the block and fragment sizes */ sblock.fs_bsize = bsize; sblock.fs_fsize = fsize; if (!POWEROF2(sblock.fs_bsize)) { printf("block size must be a power of 2, not %d\n", sblock.fs_bsize); exit(16); } if (!POWEROF2(sblock.fs_fsize)) { printf("fragment size must be a power of 2, not %d\n", sblock.fs_fsize); exit(17); } if (sblock.fs_fsize < sectorsize) { printf("fragment size %d is too small, minimum is %d\n", sblock.fs_fsize, sectorsize); exit(18); } if (sblock.fs_bsize < MINBSIZE) { printf("block size %d is too small, minimum is %d\n", sblock.fs_bsize, MINBSIZE); exit(19); } if (sblock.fs_bsize > FFS_MAXBSIZE) { printf("block size %d is too large, maximum is %d\n", sblock.fs_bsize, FFS_MAXBSIZE); exit(19); } if (sblock.fs_bsize < sblock.fs_fsize) { printf("block size (%d) cannot be smaller than fragment size (%d)\n", sblock.fs_bsize, sblock.fs_fsize); exit(20); } if (maxbsize < bsize || !POWEROF2(maxbsize)) { sblock.fs_maxbsize = sblock.fs_bsize; printf("Extent size set to %d\n", sblock.fs_maxbsize); } else if (sblock.fs_maxbsize > FS_MAXCONTIG * sblock.fs_bsize) { sblock.fs_maxbsize = FS_MAXCONTIG * sblock.fs_bsize; printf("Extent size reduced to %d\n", sblock.fs_maxbsize); } else { sblock.fs_maxbsize = maxbsize; } sblock.fs_maxcontig = maxcontig; if (sblock.fs_maxcontig < sblock.fs_maxbsize / sblock.fs_bsize) { sblock.fs_maxcontig = sblock.fs_maxbsize / sblock.fs_bsize; printf("Maxcontig raised to %d\n", sblock.fs_maxbsize); } if (sblock.fs_maxcontig > 1) sblock.fs_contigsumsize = MIN(sblock.fs_maxcontig,FS_MAXCONTIG); sblock.fs_bmask = ~(sblock.fs_bsize - 1); sblock.fs_fmask = ~(sblock.fs_fsize - 1); sblock.fs_qbmask = ~sblock.fs_bmask; sblock.fs_qfmask = ~sblock.fs_fmask; for (sblock.fs_bshift = 0, i = sblock.fs_bsize; i > 1; i >>= 1) sblock.fs_bshift++; for (sblock.fs_fshift = 0, i = sblock.fs_fsize; i > 1; i >>= 1) sblock.fs_fshift++; sblock.fs_frag = numfrags(&sblock, sblock.fs_bsize); for (sblock.fs_fragshift = 0, i = sblock.fs_frag; i > 1; i >>= 1) sblock.fs_fragshift++; if (sblock.fs_frag > MAXFRAG) { printf("fragment size %d is too small, " "minimum with block size %d is %d\n", sblock.fs_fsize, sblock.fs_bsize, sblock.fs_bsize / MAXFRAG); exit(21); } sblock.fs_fsbtodb = ilog2(sblock.fs_fsize / sectorsize); sblock.fs_size = fssize = dbtofsb(&sblock, fssize); if (Oflag <= 1) { sblock.fs_magic = FS_UFS1_MAGIC; sblock.fs_sblockloc = SBLOCK_UFS1; sblock.fs_nindir = sblock.fs_bsize / sizeof(int32_t); sblock.fs_inopb = sblock.fs_bsize / sizeof(struct ufs1_dinode); sblock.fs_maxsymlinklen = ((NDADDR + NIADDR) * sizeof (int32_t)); sblock.fs_old_inodefmt = FS_44INODEFMT; sblock.fs_old_cgoffset = 0; sblock.fs_old_cgmask = 0xffffffff; sblock.fs_old_size = sblock.fs_size; sblock.fs_old_rotdelay = 0; sblock.fs_old_rps = 60; sblock.fs_old_nspf = sblock.fs_fsize / sectorsize; sblock.fs_old_cpg = 1; sblock.fs_old_interleave = 1; sblock.fs_old_trackskew = 0; sblock.fs_old_cpc = 0; sblock.fs_old_postblformat = 1; sblock.fs_old_nrpos = 1; } else { sblock.fs_magic = FS_UFS2_MAGIC; #if 0 /* XXX makefs is used for small filesystems. */ sblock.fs_sblockloc = SBLOCK_UFS2; #else sblock.fs_sblockloc = SBLOCK_UFS1; #endif sblock.fs_nindir = sblock.fs_bsize / sizeof(int64_t); sblock.fs_inopb = sblock.fs_bsize / sizeof(struct ufs2_dinode); sblock.fs_maxsymlinklen = ((NDADDR + NIADDR) * sizeof (int64_t)); } sblock.fs_sblkno = roundup(howmany(sblock.fs_sblockloc + SBLOCKSIZE, sblock.fs_fsize), sblock.fs_frag); sblock.fs_cblkno = (daddr_t)(sblock.fs_sblkno + roundup(howmany(SBLOCKSIZE, sblock.fs_fsize), sblock.fs_frag)); sblock.fs_iblkno = sblock.fs_cblkno + sblock.fs_frag; sblock.fs_maxfilesize = sblock.fs_bsize * NDADDR - 1; for (sizepb = sblock.fs_bsize, i = 0; i < NIADDR; i++) { sizepb *= NINDIR(&sblock); sblock.fs_maxfilesize += sizepb; } /* * Calculate the number of blocks to put into each cylinder group. * * This algorithm selects the number of blocks per cylinder * group. The first goal is to have at least enough data blocks * in each cylinder group to meet the density requirement. Once * this goal is achieved we try to expand to have at least * 1 cylinder group. Once this goal is achieved, we pack as * many blocks into each cylinder group map as will fit. * * We start by calculating the smallest number of blocks that we * can put into each cylinder group. If this is too big, we reduce * the density until it fits. */ origdensity = density; for (;;) { fragsperinode = MAX(numfrags(&sblock, density), 1); minfpg = fragsperinode * INOPB(&sblock); if (minfpg > sblock.fs_size) minfpg = sblock.fs_size; sblock.fs_ipg = INOPB(&sblock); sblock.fs_fpg = roundup(sblock.fs_iblkno + sblock.fs_ipg / INOPF(&sblock), sblock.fs_frag); if (sblock.fs_fpg < minfpg) sblock.fs_fpg = minfpg; sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode), INOPB(&sblock)); sblock.fs_fpg = roundup(sblock.fs_iblkno + sblock.fs_ipg / INOPF(&sblock), sblock.fs_frag); if (sblock.fs_fpg < minfpg) sblock.fs_fpg = minfpg; sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode), INOPB(&sblock)); if (CGSIZE(&sblock) < (unsigned long)sblock.fs_bsize) break; density -= sblock.fs_fsize; } if (density != origdensity) printf("density reduced from %d to %d\n", origdensity, density); if (maxblkspercg <= 0 || maxblkspercg >= fssize) maxblkspercg = fssize - 1; /* * Start packing more blocks into the cylinder group until * it cannot grow any larger, the number of cylinder groups * drops below 1, or we reach the size requested. */ for ( ; sblock.fs_fpg < maxblkspercg; sblock.fs_fpg += sblock.fs_frag) { sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode), INOPB(&sblock)); if (sblock.fs_size / sblock.fs_fpg < 1) break; if (CGSIZE(&sblock) < (unsigned long)sblock.fs_bsize) continue; if (CGSIZE(&sblock) == (unsigned long)sblock.fs_bsize) break; sblock.fs_fpg -= sblock.fs_frag; sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode), INOPB(&sblock)); break; } /* * Check to be sure that the last cylinder group has enough blocks * to be viable. If it is too small, reduce the number of blocks * per cylinder group which will have the effect of moving more * blocks into the last cylinder group. */ optimalfpg = sblock.fs_fpg; for (;;) { sblock.fs_ncg = howmany(sblock.fs_size, sblock.fs_fpg); lastminfpg = roundup(sblock.fs_iblkno + sblock.fs_ipg / INOPF(&sblock), sblock.fs_frag); if (sblock.fs_size < lastminfpg) { printf("Filesystem size %lld < minimum size of %d\n", (long long)sblock.fs_size, lastminfpg); exit(28); } if (sblock.fs_size % sblock.fs_fpg >= lastminfpg || sblock.fs_size % sblock.fs_fpg == 0) break; sblock.fs_fpg -= sblock.fs_frag; sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode), INOPB(&sblock)); } if (optimalfpg != sblock.fs_fpg) printf("Reduced frags per cylinder group from %d to %d %s\n", optimalfpg, sblock.fs_fpg, "to enlarge last cyl group"); sblock.fs_cgsize = fragroundup(&sblock, CGSIZE(&sblock)); sblock.fs_dblkno = sblock.fs_iblkno + sblock.fs_ipg / INOPF(&sblock); if (Oflag <= 1) { sblock.fs_old_spc = sblock.fs_fpg * sblock.fs_old_nspf; sblock.fs_old_nsect = sblock.fs_old_spc; sblock.fs_old_npsect = sblock.fs_old_spc; sblock.fs_old_ncyl = sblock.fs_ncg; } /* * fill in remaining fields of the super block */ sblock.fs_csaddr = cgdmin(&sblock, 0); sblock.fs_cssize = fragroundup(&sblock, sblock.fs_ncg * sizeof(struct csum)); /* * Setup memory for temporary in-core cylgroup summaries. * Cribbed from ffs_mountfs(). */ size = sblock.fs_cssize; blks = howmany(size, sblock.fs_fsize); if (sblock.fs_contigsumsize > 0) size += sblock.fs_ncg * sizeof(int32_t); if ((space = (char *)calloc(1, size)) == NULL) err(1, "memory allocation error for cg summaries"); sblock.fs_csp = space; space = (char *)space + sblock.fs_cssize; if (sblock.fs_contigsumsize > 0) { int32_t *lp; sblock.fs_maxcluster = lp = space; for (i = 0; i < sblock.fs_ncg; i++) *lp++ = sblock.fs_contigsumsize; } sblock.fs_sbsize = fragroundup(&sblock, sizeof(struct fs)); if (sblock.fs_sbsize > SBLOCKSIZE) sblock.fs_sbsize = SBLOCKSIZE; sblock.fs_minfree = minfree; sblock.fs_maxcontig = maxcontig; sblock.fs_maxbpg = maxbpg; sblock.fs_optim = opt; sblock.fs_cgrotor = 0; sblock.fs_pendingblocks = 0; sblock.fs_pendinginodes = 0; sblock.fs_cstotal.cs_ndir = 0; sblock.fs_cstotal.cs_nbfree = 0; sblock.fs_cstotal.cs_nifree = 0; sblock.fs_cstotal.cs_nffree = 0; sblock.fs_fmod = 0; sblock.fs_ronly = 0; sblock.fs_state = 0; sblock.fs_clean = FS_ISCLEAN; sblock.fs_ronly = 0; sblock.fs_id[0] = start_time.tv_sec; sblock.fs_id[1] = randomx(); sblock.fs_fsmnt[0] = '\0'; csfrags = howmany(sblock.fs_cssize, sblock.fs_fsize); sblock.fs_dsize = sblock.fs_size - sblock.fs_sblkno - sblock.fs_ncg * (sblock.fs_dblkno - sblock.fs_sblkno); sblock.fs_cstotal.cs_nbfree = fragstoblks(&sblock, sblock.fs_dsize) - howmany(csfrags, sblock.fs_frag); sblock.fs_cstotal.cs_nffree = fragnum(&sblock, sblock.fs_size) + (fragnum(&sblock, csfrags) > 0 ? sblock.fs_frag - fragnum(&sblock, csfrags) : 0); sblock.fs_cstotal.cs_nifree = sblock.fs_ncg * sblock.fs_ipg - ROOTINO; sblock.fs_cstotal.cs_ndir = 0; sblock.fs_dsize -= csfrags; sblock.fs_time = start_time.tv_sec; if (Oflag <= 1) { sblock.fs_old_time = start_time.tv_sec; sblock.fs_old_dsize = sblock.fs_dsize; sblock.fs_old_csaddr = sblock.fs_csaddr; sblock.fs_old_cstotal.cs_ndir = sblock.fs_cstotal.cs_ndir; sblock.fs_old_cstotal.cs_nbfree = sblock.fs_cstotal.cs_nbfree; sblock.fs_old_cstotal.cs_nifree = sblock.fs_cstotal.cs_nifree; sblock.fs_old_cstotal.cs_nffree = sblock.fs_cstotal.cs_nffree; } /* * Dump out summary information about filesystem. */ #define B2MBFACTOR (1 / (1024.0 * 1024.0)) printf("%s: %.1fMB (%lld sectors) block size %d, " "fragment size %d\n", fsys, (float)sblock.fs_size * sblock.fs_fsize * B2MBFACTOR, (long long)fsbtodb(&sblock, sblock.fs_size), sblock.fs_bsize, sblock.fs_fsize); printf("\tusing %d cylinder groups of %.2fMB, %d blks, " "%d inodes.\n", sblock.fs_ncg, (float)sblock.fs_fpg * sblock.fs_fsize * B2MBFACTOR, sblock.fs_fpg / sblock.fs_frag, sblock.fs_ipg); #undef B2MBFACTOR /* * Now determine how wide each column will be, and calculate how * many columns will fit in a 76 char line. 76 is the width of the * subwindows in sysinst. */ printcolwidth = count_digits( fsbtodb(&sblock, cgsblock(&sblock, sblock.fs_ncg -1))); nprintcols = 76 / (printcolwidth + 2); /* * allocate space for superblock, cylinder group map, and * two sets of inode blocks. */ if (sblock.fs_bsize < SBLOCKSIZE) iobufsize = SBLOCKSIZE + 3 * sblock.fs_bsize; else iobufsize = 4 * sblock.fs_bsize; if ((iobuf = calloc(1, iobufsize)) == 0) { printf("Cannot allocate I/O buffer\n"); exit(38); } /* * Make a copy of the superblock into the buffer that we will be * writing out in each cylinder group. */ memcpy(writebuf, &sblock, sbsize); if (fsopts->needswap) ffs_sb_swap(&sblock, (struct fs*)writebuf); memcpy(iobuf, writebuf, SBLOCKSIZE); printf("super-block backups (for fsck -b #) at:"); for (cylno = 0; cylno < sblock.fs_ncg; cylno++) { initcg(cylno, start_time.tv_sec, fsopts); if (cylno % nprintcols == 0) printf("\n"); printf(" %*lld,", printcolwidth, (long long)fsbtodb(&sblock, cgsblock(&sblock, cylno))); fflush(stdout); } printf("\n"); /* * Now construct the initial filesystem, * then write out the super-block. */ sblock.fs_time = start_time.tv_sec; if (Oflag <= 1) { sblock.fs_old_cstotal.cs_ndir = sblock.fs_cstotal.cs_ndir; sblock.fs_old_cstotal.cs_nbfree = sblock.fs_cstotal.cs_nbfree; sblock.fs_old_cstotal.cs_nifree = sblock.fs_cstotal.cs_nifree; sblock.fs_old_cstotal.cs_nffree = sblock.fs_cstotal.cs_nffree; } if (fsopts->needswap) sblock.fs_flags |= FS_SWAPPED; ffs_write_superblock(&sblock, fsopts); return (&sblock); } /* * Write out the superblock and its duplicates, * and the cylinder group summaries */ void ffs_write_superblock(struct fs *fs, const fsinfo_t *fsopts) { int cylno, size, blks, i, saveflag; void *space; char *wrbuf; saveflag = fs->fs_flags & FS_INTERNAL; fs->fs_flags &= ~FS_INTERNAL; memcpy(writebuf, &sblock, sbsize); if (fsopts->needswap) ffs_sb_swap(fs, (struct fs*)writebuf); #ifdef __MirBSD__ arc4random_buf(((struct fs *)writebuf)->fs_historic_start, sizeof(((struct fs *)writebuf)->fs_historic_start)); #endif ffs_wtfs(fs->fs_sblockloc / sectorsize, sbsize, writebuf, fsopts); /* Write out the duplicate super blocks */ for (cylno = 0; cylno < fs->fs_ncg; cylno++) { #ifdef __MirBSD__ ((struct fs *)writebuf)->fs_unused_1 = arc4random(); #endif ffs_wtfs(fsbtodb(fs, cgsblock(fs, cylno)), sbsize, writebuf, fsopts); } /* Write out the cylinder group summaries */ size = fs->fs_cssize; blks = howmany(size, fs->fs_fsize); space = (void *)fs->fs_csp; if ((wrbuf = malloc(size)) == NULL) err(1, "ffs_write_superblock: malloc %d", size); for (i = 0; i < blks; i+= fs->fs_frag) { size = fs->fs_bsize; if (i + fs->fs_frag > blks) size = (blks - i) * fs->fs_fsize; if (fsopts->needswap) ffs_csum_swap((struct csum *)space, (struct csum *)wrbuf, size); else memcpy(wrbuf, space, (u_int)size); ffs_wtfs(fsbtodb(fs, fs->fs_csaddr + i), size, wrbuf, fsopts); space = (char *)space + size; } free(wrbuf); fs->fs_flags |= saveflag; } /* * Initialize a cylinder group. */ static void initcg(int cylno, time_t utime, const fsinfo_t *fsopts) { daddr_t cbase, dmax; int32_t i, j, d, dlower, dupper, blkno; uint32_t k; struct ufs1_dinode *dp1; struct ufs2_dinode *dp2; int start; /* * Determine block bounds for cylinder group. * Allow space for super block summary information in first * cylinder group. */ cbase = cgbase(&sblock, cylno); dmax = cbase + sblock.fs_fpg; if (dmax > sblock.fs_size) dmax = sblock.fs_size; dlower = cgsblock(&sblock, cylno) - cbase; dupper = cgdmin(&sblock, cylno) - cbase; if (cylno == 0) dupper += howmany(sblock.fs_cssize, sblock.fs_fsize); memset(&acg, 0, sblock.fs_cgsize); acg.cg_time = utime; acg.cg_magic = CG_MAGIC; acg.cg_cgx = cylno; acg.cg_niblk = sblock.fs_ipg; acg.cg_initediblk = sblock.fs_ipg < 2 * INOPB(&sblock) ? sblock.fs_ipg : 2 * INOPB(&sblock); acg.cg_ndblk = dmax - cbase; if (sblock.fs_contigsumsize > 0) acg.cg_nclusterblks = acg.cg_ndblk >> sblock.fs_fragshift; start = &acg.cg_space[0] - (u_char *)(&acg.cg_firstfield); if (Oflag == 2) { acg.cg_iusedoff = start; } else { if (cylno == sblock.fs_ncg - 1) acg.cg_old_ncyl = howmany(acg.cg_ndblk, sblock.fs_fpg / sblock.fs_old_cpg); else acg.cg_old_ncyl = sblock.fs_old_cpg; acg.cg_old_time = acg.cg_time; acg.cg_time = 0; acg.cg_old_niblk = acg.cg_niblk; acg.cg_niblk = 0; acg.cg_initediblk = 0; acg.cg_old_btotoff = start; acg.cg_old_boff = acg.cg_old_btotoff + sblock.fs_old_cpg * sizeof(int32_t); acg.cg_iusedoff = acg.cg_old_boff + sblock.fs_old_cpg * sizeof(u_int16_t); } acg.cg_freeoff = acg.cg_iusedoff + howmany(sblock.fs_ipg, CHAR_BIT); if (sblock.fs_contigsumsize <= 0) { acg.cg_nextfreeoff = acg.cg_freeoff + howmany(sblock.fs_fpg, CHAR_BIT); } else { acg.cg_clustersumoff = acg.cg_freeoff + howmany(sblock.fs_fpg, CHAR_BIT) - sizeof(int32_t); acg.cg_clustersumoff = roundup(acg.cg_clustersumoff, sizeof(int32_t)); acg.cg_clusteroff = acg.cg_clustersumoff + (sblock.fs_contigsumsize + 1) * sizeof(int32_t); acg.cg_nextfreeoff = acg.cg_clusteroff + howmany(fragstoblks(&sblock, sblock.fs_fpg), CHAR_BIT); } if (acg.cg_nextfreeoff > sblock.fs_cgsize) { printf("Panic: cylinder group too big\n"); exit(37); } acg.cg_cs.cs_nifree += sblock.fs_ipg; if (cylno == 0) for (k = 0; k < ROOTINO; k++) { setbit(cg_inosused(&acg, 0), k); acg.cg_cs.cs_nifree--; } if (cylno > 0) { /* * In cylno 0, beginning space is reserved * for boot and super blocks. */ for (d = 0, blkno = 0; d < dlower;) { ffs_setblock(&sblock, cg_blksfree(&acg, 0), blkno); if (sblock.fs_contigsumsize > 0) setbit(cg_clustersfree(&acg, 0), blkno); acg.cg_cs.cs_nbfree++; d += sblock.fs_frag; blkno++; } } if ((i = (dupper & (sblock.fs_frag - 1))) != 0) { acg.cg_frsum[sblock.fs_frag - i]++; for (d = dupper + sblock.fs_frag - i; dupper < d; dupper++) { setbit(cg_blksfree(&acg, 0), dupper); acg.cg_cs.cs_nffree++; } } for (d = dupper, blkno = dupper >> sblock.fs_fragshift; d + sblock.fs_frag <= acg.cg_ndblk; ) { ffs_setblock(&sblock, cg_blksfree(&acg, 0), blkno); if (sblock.fs_contigsumsize > 0) setbit(cg_clustersfree(&acg, 0), blkno); acg.cg_cs.cs_nbfree++; d += sblock.fs_frag; blkno++; } if (d < acg.cg_ndblk) { acg.cg_frsum[acg.cg_ndblk - d]++; for (; d < acg.cg_ndblk; d++) { setbit(cg_blksfree(&acg, 0), d); acg.cg_cs.cs_nffree++; } } if (sblock.fs_contigsumsize > 0) { int32_t *sump = cg_clustersum(&acg, 0); u_char *mapp = cg_clustersfree(&acg, 0); int map = *mapp++; int bit = 1; int run = 0; for (i = 0; i < acg.cg_nclusterblks; i++) { if ((map & bit) != 0) { run++; } else if (run != 0) { if (run > sblock.fs_contigsumsize) run = sblock.fs_contigsumsize; sump[run]++; run = 0; } if ((i & (CHAR_BIT - 1)) != (CHAR_BIT - 1)) { bit <<= 1; } else { map = *mapp++; bit = 1; } } if (run != 0) { if (run > sblock.fs_contigsumsize) run = sblock.fs_contigsumsize; sump[run]++; } } sblock.fs_cs(&sblock, cylno) = acg.cg_cs; /* * Write out the duplicate super block, the cylinder group map * and two blocks worth of inodes in a single write. */ start = sblock.fs_bsize > SBLOCKSIZE ? sblock.fs_bsize : SBLOCKSIZE; memcpy(&iobuf[start], &acg, sblock.fs_cgsize); if (fsopts->needswap) ffs_cg_swap(&acg, (struct cg*)&iobuf[start], &sblock); start += sblock.fs_bsize; dp1 = (struct ufs1_dinode *)(&iobuf[start]); dp2 = (struct ufs2_dinode *)(&iobuf[start]); for (i = 0; i < acg.cg_initediblk; i++) { if (sblock.fs_magic == FS_UFS1_MAGIC) { /* No need to swap, it'll stay random */ dp1->di_gen = randomx(); dp1++; } else { dp2->di_gen = randomx(); dp2++; } } ffs_wtfs(fsbtodb(&sblock, cgsblock(&sblock, cylno)), iobufsize, iobuf, fsopts); /* * For the old filesystem, we have to initialize all the inodes. */ if (Oflag <= 1) { for (i = 2 * sblock.fs_frag; i < sblock.fs_ipg / INOPF(&sblock); i += sblock.fs_frag) { dp1 = (struct ufs1_dinode *)(&iobuf[start]); for (j = 0; j < INOPB(&sblock); j++) { dp1->di_gen = randomx(); dp1++; } ffs_wtfs(fsbtodb(&sblock, cgimin(&sblock, cylno) + i), sblock.fs_bsize, &iobuf[start], fsopts); } } } /* * read a block from the filesystem */ void ffs_rdfs(daddr_t bno, int size, void *bf, const fsinfo_t *fsopts) { int n; off_t offset; offset = bno; offset *= fsopts->sectorsize; if (lseek(fsopts->fd, offset, SEEK_SET) < 0) err(1, "ffs_rdfs: seek error for sector %lld: %s\n", (long long)bno, strerror(errno)); n = read(fsopts->fd, bf, size); if (n == -1) { abort(); err(1, "ffs_rdfs: read error bno %lld size %d", (long long)bno, size); } else if (n != size) errx(1, "ffs_rdfs: read error for sector %lld: %s\n", (long long)bno, strerror(errno)); } /* * write a block to the filesystem */ void ffs_wtfs(daddr_t bno, int size, void *bf, const fsinfo_t *fsopts) { int n; off_t offset; offset = bno; offset *= fsopts->sectorsize; if (lseek(fsopts->fd, offset, SEEK_SET) < 0) err(1, "wtfs: seek error for sector %lld: %s\n", (long long)bno, strerror(errno)); n = write(fsopts->fd, bf, size); if (n == -1) err(1, "wtfs: write error for sector %lld: %s\n", (long long)bno, strerror(errno)); else if (n != size) errx(1, "wtfs: write error for sector %lld: %s\n", (long long)bno, strerror(errno)); } /* Determine how many digits are needed to print a given integer */ static int count_digits(int num) { int ndig; for(ndig = 1; num > 9; num /=10, ndig++); return (ndig); } static int ilog2(int val) { u_int n; for (n = 0; n < sizeof(n) * CHAR_BIT; n++) if (1 << n == val) return (n); errx(1, "ilog2: %d is not a power of 2\n", val); } makefs/src/usr.sbin/makefs/ffs/newfs_extern.h010064400000000000000000000032731134453732000165200ustar00/* $NetBSD: newfs_extern.h,v 1.3 2009/10/21 01:07:47 snj Exp $ */ /* From: NetBSD: extern.h,v 1.3 2000/12/01 12:03:27 simonb Exp $ */ /* * Copyright (c) 1997 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. */ /* prototypes */ struct fs *ffs_mkfs(const char *, const fsinfo_t *); void ffs_write_superblock(struct fs *, const fsinfo_t *); void ffs_rdfs(daddr_t, int, void *, const fsinfo_t *); void ffs_wtfs(daddr_t, int, void *, const fsinfo_t *); #define FFS_MAXBSIZE 65536 makefs/src/usr.sbin/makefs/ffs/ufs_bmap.c010064400000000000000000000111761045275301400156000ustar00/* $NetBSD: ufs_bmap.c,v 1.16 2005/10/08 03:21:17 chs Exp $ */ /* From: NetBSD: ufs_bmap.c,v 1.14 2001/11/08 05:00:51 chs Exp */ /* * Copyright (c) 1989, 1991, 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. * * @(#)ufs_bmap.c 8.8 (Berkeley) 8/11/95 */ #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #include #if defined(__RCSID) && !defined(__lint) __RCSID("$NetBSD: ufs_bmap.c,v 1.16 2005/10/08 03:21:17 chs Exp $"); #endif /* !__lint */ #include #include #include #include #include #include "makefs.h" #include #include #include #include "ffs/ufs_inode.h" #include "ffs/ffs_extern.h" /* * Create an array of logical block number/offset pairs which represent the * path of indirect blocks required to access a data block. The first "pair" * contains the logical block number of the appropriate single, double or * triple indirect block and the offset into the inode indirect block array. * Note, the logical block number of the inode single/double/triple indirect * block appears twice in the array, once with the offset into the i_ffs_ib and * once with the offset into the page itself. */ int ufs_getlbns(struct inode *ip, daddr_t bn, struct indir *ap, int *nump) { daddr_t metalbn, realbn; int64_t blockcnt; int lbc; int i, numlevels, off; u_long lognindir; lognindir = ffs(NINDIR(ip->i_fs)) - 1; if (nump) *nump = 0; numlevels = 0; realbn = bn; if ((long)bn < 0) bn = -(long)bn; assert (bn >= NDADDR); /* * Determine the number of levels of indirection. After this loop * is done, blockcnt indicates the number of data blocks possible * at the given level of indirection, and NIADDR - i is the number * of levels of indirection needed to locate the requested block. */ bn -= NDADDR; for (lbc = 0, i = NIADDR;; i--, bn -= blockcnt) { if (i == 0) return (EFBIG); lbc += lognindir; blockcnt = (int64_t)1 << lbc; if (bn < blockcnt) break; } /* Calculate the address of the first meta-block. */ metalbn = -((realbn >= 0 ? realbn : -realbn) - bn + NIADDR - i); /* * At each iteration, off is the offset into the bap array which is * an array of disk addresses at the current level of indirection. * The logical block number and the offset in that block are stored * into the argument array. */ ap->in_lbn = metalbn; ap->in_off = off = NIADDR - i; ap->in_exists = 0; ap++; for (++numlevels; i <= NIADDR; i++) { /* If searching for a meta-data block, quit when found. */ if (metalbn == realbn) break; lbc -= lognindir; blockcnt = (int64_t)1 << lbc; off = (bn >> lbc) & (NINDIR(ip->i_fs) - 1); ++numlevels; ap->in_lbn = metalbn; ap->in_off = off; ap->in_exists = 0; ++ap; metalbn -= -1 + (off << lbc); } if (nump) *nump = numlevels; return (0); } makefs/src/usr.sbin/makefs/ffs/ufs_inode.h010064400000000000000000000110511314214553200157530ustar00/* $NetBSD: ufs_inode.h,v 1.4 2005/06/23 00:53:16 fvdl Exp $ */ /* From: NetBSD: inode.h,v 1.27 2001/12/18 10:57:23 fvdl Exp $ */ /* * Copyright (c) 1982, 1989, 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. * * @(#)inode.h 8.9 (Berkeley) 5/14/95 */ union dinode { struct ufs1_dinode ffs1_din; struct ufs2_dinode ffs2_din; }; struct inode { ino_t i_number; /* The identity of the inode. */ struct fs *i_fs; /* Filesystem */ union dinode i_din; int i_fd; /* File descriptor */ uint64_t i_size; }; #define i_ffs1_atime i_din.ffs1_din.di_atime #define i_ffs1_atimensec i_din.ffs1_din.di_atimensec #define i_ffs1_blocks i_din.ffs1_din.di_blocks #define i_ffs1_ctime i_din.ffs1_din.di_ctime #define i_ffs1_ctimensec i_din.ffs1_din.di_ctimensec #define i_ffs1_db i_din.ffs1_din.di_db #define i_ffs1_flags i_din.ffs1_din.di_flags #define i_ffs1_gen i_din.ffs1_din.di_gen #define i_ffs11_gid i_din.ffs1_din.di_gid #define i_ffs1_ib i_din.ffs1_din.di_ib #define i_ffs1_mode i_din.ffs1_din.di_mode #define i_ffs1_mtime i_din.ffs1_din.di_mtime #define i_ffs1_mtimensec i_din.ffs1_din.di_mtimensec #define i_ffs1_nlink i_din.ffs1_din.di_nlink #define i_ffs1_rdev i_din.ffs1_din.di_rdev #define i_ffs1_shortlink i_din.ffs1_din.db #define i_ffs1_size i_din.ffs1_din.di_size #define i_ffs1_uid i_din.ffs1_din.di_uid #define i_ffs2_atime i_din.ffs2_din.di_atime #define i_ffs2_atimensec i_din.ffs2_din.di_atimensec #define i_ffs2_blocks i_din.ffs2_din.di_blocks #define i_ffs2_ctime i_din.ffs2_din.di_ctime #define i_ffs2_ctimensec i_din.ffs2_din.di_ctimensec #define i_ffs2_birthtime i_din.ffs2_din.di_birthtime #define i_ffs2_birthnsec i_din.ffs2_din.di_birthnsec #define i_ffs2_db i_din.ffs2_din.di_db #define i_ffs2_flags i_din.ffs2_din.di_flags #define i_ffs2_gen i_din.ffs2_din.di_gen #define i_ffs21_gid i_din.ffs2_din.di_gid #define i_ffs2_ib i_din.ffs2_din.di_ib #define i_ffs2_mode i_din.ffs2_din.di_mode #define i_ffs2_mtime i_din.ffs2_din.di_mtime #define i_ffs2_mtimensec i_din.ffs2_din.di_mtimensec #define i_ffs2_nlink i_din.ffs2_din.di_nlink #define i_ffs2_rdev i_din.ffs2_din.di_rdev #define i_ffs2_shortlink i_din.ffs2_din.db #define i_ffs2_size i_din.ffs2_din.di_size #define i_ffs2_uid i_din.ffs2_din.di_uid #undef DIP #define DIP(ip, field) \ (((ip)->i_fs->fs_magic == FS_UFS1_MAGIC) ? \ (ip)->i_ffs1_##field : (ip)->i_ffs2_##field) #define DIP_ASSIGN(ip, field, value) \ do { \ if ((ip)->i_fs->fs_magic == FS_UFS1_MAGIC) \ (ip)->i_ffs1_##field = (value); \ else \ (ip)->i_ffs2_##field = (value); \ } while(0) #define DIP_ADD(ip, field, value) \ do { \ if ((ip)->i_fs->fs_magic == FS_UFS1_MAGIC) \ (ip)->i_ffs1_##field += (value); \ else \ (ip)->i_ffs2_##field += (value); \ } while(0) makefs/src/usr.sbin/makefs/makefs.8010064400000000000000000000347601341050352200144170ustar00.\" $MirOS: src/usr.sbin/makefs/makefs.8,v 1.18 2018/12/25 19:38:31 tg Exp $ .\" $NetBSD: makefs.8,v 1.32 2009/01/20 20:47:25 bjh21 Exp $ .\" .\" Copyright (c) 2001-2003 Wasabi Systems, Inc. .\" All rights reserved. .\" .\" Written by Luke Mewburn 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. .\"- .\" Copyright (c) 2008, 2009, 2010, 2016, 2018 .\" mirabilos .\"- .\" Try to make GNU groff and AT&T nroff more compatible .\" * ` generates ‘ in gnroff, so use \` .\" * ' generates ’ in gnroff, \' generates ´, so use \*(aq .\" * - generates ‐ in gnroff, \- generates −, so .tr it to - .\" thus use - for hyphens and \- for minus signs and option dashes .\" * ~ is size-reduced and placed atop in groff, so use \*(TI .\" * ^ is size-reduced and placed atop in groff, so use \*(ha .\" * \(en does not work in nroff, so use \*(en .\" * <>| are problematic, so redefine and use \*(Lt\*(Gt\*(Ba .\" Also make sure to use \& *before* a punctuation char that is to not .\" be interpreted as punctuation, and especially with two-letter words .\" but also (after) a period that does not end a sentence (“e.g.\&”). .\" The section after the "doc" macropackage has been loaded contains .\" additional code to convene between the UCB mdoc macropackage (and .\" its variant as BSD mdoc in groff) and the GNU mdoc macropackage. .\" .ie \n(.g \{\ . if \*[.T]ascii .tr \-\N'45' . if \*[.T]latin1 .tr \-\N'45' . if \*[.T]utf8 .tr \-\N'45' . ds <= \[<=] . ds >= \[>=] . ds Rq \[rq] . ds Lq \[lq] . ds sL \(aq . ds sR \(aq . if \*[.T]utf8 .ds sL ` . if \*[.T]ps .ds sL ` . if \*[.T]utf8 .ds sR ' . if \*[.T]ps .ds sR ' . ds aq \(aq . ds TI \(ti . ds ha \(ha . ds en \(en .\} .el \{\ . ds aq ' . ds TI ~ . ds ha ^ . ds en \(em .\} .\" .\" Implement .Dd with the Mdocdate RCS keyword .\" .rn Dd xD .de Dd .ie \\$1$Mdocdate: \{\ . xD \\$2 \\$3, \\$4 .\} .el .xD \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 .. .\" .\" .Dd must come before definition of .Mx, because when called .\" with -mandoc, it might implement .Mx itself, but we want to .\" use our own definition. And .Dd must come *first*, always. .\" .Dd $Mdocdate: December 25 2018 $ .\" .\" Check which macro package we use, and do other -mdoc setup. .\" .ie \n(.g \{\ . if \*[.T]utf8 .tr \[la]\*(Lt . if \*[.T]utf8 .tr \[ra]\*(Gt . ie d volume-ds-1 .ds tT gnu . el .ie d doc-volume-ds-1 .ds tT gnp . el .ds tT bsd .\} .el .ds tT ucb .\" .\" Implement .Mx (MirBSD) .\" .ie "\*(tT"gnu" \{\ . eo . de Mx . nr curr-font \n[.f] . nr curr-size \n[.ps] . ds str-Mx \f[\n[curr-font]]\s[\n[curr-size]u] . ds str-Mx1 \*[Tn-font-size]\%MirBSD\*[str-Mx] . if !\n[arg-limit] \ . if \n[.$] \{\ . ds macro-name Mx . parse-args \$@ . \} . if (\n[arg-limit] > \n[arg-ptr]) \{\ . nr arg-ptr +1 . ie (\n[type\n[arg-ptr]] == 2) \ . as str-Mx1 \~\*[arg\n[arg-ptr]] . el \ . nr arg-ptr -1 . \} . ds arg\n[arg-ptr] "\*[str-Mx1] . nr type\n[arg-ptr] 2 . ds space\n[arg-ptr] "\*[space] . nr num-args (\n[arg-limit] - \n[arg-ptr]) . nr arg-limit \n[arg-ptr] . if \n[num-args] \ . parse-space-vector . print-recursive .. . ec . ds sP \s0 . ds tN \*[Tn-font-size] .\} .el .ie "\*(tT"gnp" \{\ . eo . de Mx . nr doc-curr-font \n[.f] . nr doc-curr-size \n[.ps] . ds doc-str-Mx \f[\n[doc-curr-font]]\s[\n[doc-curr-size]u] . ds doc-str-Mx1 \*[doc-Tn-font-size]\%MirBSD\*[doc-str-Mx] . if !\n[doc-arg-limit] \ . if \n[.$] \{\ . ds doc-macro-name Mx . doc-parse-args \$@ . \} . if (\n[doc-arg-limit] > \n[doc-arg-ptr]) \{\ . nr doc-arg-ptr +1 . ie (\n[doc-type\n[doc-arg-ptr]] == 2) \ . as doc-str-Mx1 \~\*[doc-arg\n[doc-arg-ptr]] . el \ . nr doc-arg-ptr -1 . \} . ds doc-arg\n[doc-arg-ptr] "\*[doc-str-Mx1] . nr doc-type\n[doc-arg-ptr] 2 . ds doc-space\n[doc-arg-ptr] "\*[doc-space] . nr doc-num-args (\n[doc-arg-limit] - \n[doc-arg-ptr]) . nr doc-arg-limit \n[doc-arg-ptr] . if \n[doc-num-args] \ . doc-parse-space-vector . doc-print-recursive .. . ec . ds sP \s0 . ds tN \*[doc-Tn-font-size] .\} .el \{\ . de Mx . nr cF \\n(.f . nr cZ \\n(.s . ds aa \&\f\\n(cF\s\\n(cZ . if \\n(aC==0 \{\ . ie \\n(.$==0 \&MirBSD\\*(aa . el .aV \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 . \} . if \\n(aC>\\n(aP \{\ . nr aP \\n(aP+1 . ie \\n(C\\n(aP==2 \{\ . as b1 \&MirBSD\ #\&\\*(A\\n(aP\\*(aa . ie \\n(aC>\\n(aP \{\ . nr aP \\n(aP+1 . nR . \} . el .aZ . \} . el \{\ . as b1 \&MirBSD\\*(aa . nR . \} . \} .. .\} .\"- .Dt MAKEFS 8 .Os .Sh NAME .Nm makefs .Nd create a filesystem image from a directory tree .Sh SYNOPSIS .Nm .Op Fl x .Op Fl B Ar byte-order .Op Fl b Ar free-blocks .Op Fl d Ar debug-mask .Op Fl F Ar specfile .Op Fl f Ar free-files .Op Fl M Ar minimum-size .Op Fl m Ar maximum-size .Op Fl N Ar userdb-dir .Op Fl o Ar fs-options .Op Fl S Ar sector-size .Op Fl s Ar image-size .Op Fl t Ar fs-type .Op Fl T Ar maximum-time .Ar image-file .Ar directory .Sh DESCRIPTION The utility .Nm creates a filesystem image into .Ar image-file from the directory tree .Ar directory . No special devices or privileges are required to perform this task. .Pp The options are as follows: .Bl -tag -width flag .It Fl B Ar byte-order Set the byte order of the image to .Ar byte-order . Valid byte orders are .Ql 4321 , .Ql big , or .Ql be for big endian, and .Ql 1234 , .Ql little , or .Ql le for little endian. Some filesystems may have a fixed byte order; in those cases this argument will be ignored. .It Fl b Ar free-blocks Ensure that a minimum of .Ar free-blocks free blocks exist in the image. An optional .Ql % suffix may be provided to indicate that .Ar free-blocks indicates a percentage of the calculated image size. .It Fl d Ar debug-mask Enable various levels of debugging, depending upon which bits are set in .Ar debug-mask . XXX: document these .It Fl F Ar specfile Use .Ar specfile as an .Xr mtree 8 .Sq specfile specification. .Pp If a specfile entry exists in the underlying filesystem, its permissions and modification time will be used unless specifically overridden by the specfile. An error will be raised if the type of entry in the specfile conflicts with that of an existing entry. .Pp In the opposite case (where a specfile entry does not have an entry in the underlying filesystem) the following occurs: If the specfile entry is marked .Sy optional , the specfile entry is ignored. Otherwise, the entry will be created in the image, and it is necessary to specify at least the following parameters in the specfile: .Sy type , .Sy mode , .Sy gname , or .Sy gid , and .Sy uname or .Sy uid , .Sy device (in the case of block or character devices), and .Sy link (in the case of symbolic links). If .Sy time isn't provided, the current time will be used. If .Sy flags isn't provided, the current file flags will be used. Missing regular file entries will be created as zero-length files. .It Fl f Ar free-files Ensure that a minimum of .Ar free-files free files (inodes) exist in the image. An optional .Ql % suffix may be provided to indicate that .Ar free-files indicates a percentage of the calculated image size. .It Fl M Ar minimum-size Set the minimum size of the filesystem image to .Ar minimum-size . .It Fl m Ar maximum-size Set the maximum size of the filesystem image to .Ar maximum-size . An error will be raised if the target filesystem needs to be larger than this to accommodate the provided directory tree. .It Fl N Ar dbdir Use the user database text file .Pa master.passwd and group database text file .Pa group from .Ar dbdir , rather than using the results from the system's .Xr getpwnam 3 and .Xr getgrnam 3 (and related) library calls. .It Fl o Ar fs-options Set filesystem specific options. .Ar fs-options is a comma separated list of options. Valid filesystem specific options are detailed below. .It Fl S Ar sector-size Set the filesystem sector size to .Ar sector-size . .\" XXX: next line also true for cd9660? Defaults to 512. .It Fl s Ar image-size Set the size of the filesystem image to .Ar image-size . .It Fl t Ar fs-type Create an .Ar fs-type filesystem image. The following filesystem types are supported: .Bl -tag -width cd9660 -offset indent .It Sy ffs BSD fast filesystem (default). .It Sy cd9660 ISO 9660 filesystem. .El .It Fl T Ar maximum-time Clamp superblock and file timestamps to .Ar maximum-time seconds since the Epoch. .It Fl x Exclude filesystem nodes not explicitly listed in the specfile. .El .Pp Where sizes are specified, a decimal number of bytes is expected. Two or more numbers may be separated by an .Dq x to indicate a product. Each number may have one of the following optional suffixes: .Bl -tag -width 3n -offset indent -compact .It b Block; multiply by 512 .It k Kibi; multiply by 1024 (1 KiB) .It m Mebi; multiply by 1048576 (1 MiB) .It g Gibi; multiply by 1073741824 (1 GiB) .It t Tebi; multiply by 1099511627776 (1 TiB) .It w Word; multiply by the number of bytes in an integer .El .\" .\" .Ss FFS-specific options .Sy ffs images have ffs-specific optional parameters that may be provided. Each of the options consists of a keyword, an equal sign .Pq Ql = , and a value. The following keywords are supported: .Pp .Bl -tag -width optimization -offset indent -compact .It Sy avgfilesize Expected average file size. .It Sy avgfpdir Expected number of files per directory. .It Sy bsize Block size. .It Sy density Bytes per inode. .It Sy fsize Fragment size. .It Sy maxbpg Maximum blocks per file in a cylinder group. .It Sy minfree Minimum % free. .It Sy optimization Optimization preference; one of .Ql space or .Ql time . .It Sy extent Maximum extent size. .It Sy maxbpcg Maximum total number of blocks in a cylinder group. .It Sy version UFS version. 1 for FFS (default), 2 for UFS2. .El .Ss CD9660-specific options .Sy cd9660 images have ISO9660-specific optional parameters that may be provided. The arguments consist of a keyword and, optionally, an equal sign .Pq Ql = , and a value. The following keywords are supported: .Pp .Bl -tag -width omit-trailing-period -offset indent -compact .It Sy allow\-deep\-trees Allow the directory structure to exceed the maximum specified in the spec. .\" .It Sy allow\-illegal\-chars .\" Unknown .\" .It Sy allow\-lowercase .\" Unknown .It Sy allow\-max\-name Allow 37 instead of 33 characters for filenames by omitting the version id. .It Sy allow\-multidot Allow multiple dots in a filename. .It Sy applicationid Application ID of the image. .It Sy archimedes Use the .Ql ARCHIMEDES extension to encode .Tn RISC OS metadata. .It Sy boot\-info\-table Write a legacy 56-byte table at offset 8 into the boot image .Pq see below . .It Sy boot\-load\-segment Set load segment for the boot image. .It Sy bootimage Filename of a boot image in the format .Dq sysid;filename , where .Dq sysid is one of .Ql i386 , .Ql mac68k , .Ql macppc , or .Ql powerpc . .It Sy creation\-date Override PVD creation date. .It Sy effective\-date Override PVD effective date. .It Sy expiration\-date Override PVD expiration date. .It Sy generic\-bootimage Load a generic boot image into the first 32K of the cd9660 image. .It Sy hard\-disk\-boot Boot image is a hard disk image. .It Sy hide\-rr\-moved Assign the .Pa RR_MOVED directory a rock ridge name of the empty string instead of the default .Pa .rr_moved . .It Sy keep\-bad\-images Don't throw away images whose write was aborted due to an error. For debugging purposes. .It Sy label Label name of the image. .It Sy modification\-date Override PVD modification date. .It Sy no\-boot Boot image is not bootable. .It Sy no\-emul\-boot Boot image is a .Dq no emulation ElTorito image. .It Sy no\-trailing\-padding Do not pad the image (apparently Linux needs the padding). .It Sy omit\-trailing\-period Violate the standard, do not append a trailing period to filenames without an extension. .It Sy preparer Preparer ID of the image. .It Sy publisher Publisher ID of the image. .It Sy rockridge Use RockRidge extensions (for longer filenames, etc.). .It Sy rr\-squash Force uid 0, gid 0, and rationalised permission bits for RockRidge entries. .It Sy volumeid Volume set identifier of the image. .El .Pp The .Sy boot\-info\-table currently consists of the following fields (all 7.3.1 numbers), offsets relative to the boot image: .Bl -tag -width XXX -offset indent -compact .It Sy 0 8 bytes: kept as is, not part of checksum .It Sy 8 LBA of PVD .It Sy 12 LBA of boot image .It Sy 16 Size in bytes of boot image .It Sy 20 32-bit additive sum of all 32-bit words of boot image .It Sy 24 40 reserved bytes (MBZ) .It Sy 64 Begin of checksummed data, kept as is .El .Pp Dates (to override) are in 8.4.26.1 format .Pq Ar YYYYmmddHHMMSS00 and Universal Time, i.e. with zero offset from Greenwich Mean Time. .Sh SEE ALSO .Xr strsuftoll 3 , .Xr installboot 8 , .Xr mtree 8 , .Xr newfs 8 .Sh HISTORY The .Nm utility appeared in .Nx 1.6 . .Pp Support for overriding PVD dates and the boot info table was added in .Mx 11 . .Sh AUTHORS .An Luke Mewburn .Aq lukem@NetBSD.org (original program) .An Daniel Watt , .An Walter Deignan , .An Ryan Gabrys , .An Alan Perez-Rathke , .An Ram Vedam (cd9660 support) .An Thorsten Glaser Aq tg@mirbsd.org .Sh CAVEATS .Nm may be limited to images less than 2 GiB in size due to internal use of the long type. makefs/src/usr.sbin/makefs/makefs.c010064400000000000000000000213101341415245600144670ustar00/* $NetBSD: makefs.c,v 1.26 2006/10/22 21:11:56 christos Exp $ */ /* * Copyright (c) 2009, 2010 * Thorsten Glaser * Copyright (c) 2001-2003 Wasabi Systems, Inc. * All rights reserved. * * Written by Luke Mewburn 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. */ #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #if defined(__MirBSD__) || defined(GNUPORT) #include "mbsdtree.h" #endif #include #if defined(__RCSID) && !defined(__lint) __RCSID("$NetBSD: makefs.c,v 1.26 2006/10/22 21:11:56 christos Exp $"); __IDSTRING(mbsdid, "$MirOS: src/usr.sbin/makefs/makefs.c,v 1.17 2019/01/05 16:02:27 tg Exp $"); #endif /* !__lint */ #include #include #include #include #include #include #include #include #include #include #include "makefs.h" #include "mtree.h" #include "cd9660.h" /* * list of supported filesystems and dispatch functions */ typedef struct { const char *type; void (*prepare_options)(fsinfo_t *); int (*parse_options)(const char *, fsinfo_t *); void (*cleanup_options)(fsinfo_t *); void (*make_fs)(const char *, const char *, fsnode *, fsinfo_t *); } fstype_t; static fstype_t fstypes[] = { { "ffs", ffs_prep_opts, ffs_parse_opts, ffs_cleanup_opts, ffs_makefs }, { "cd9660", cd9660_prep_opts, cd9660_parse_opts, cd9660_cleanup_opts, cd9660_makefs}, { .type = NULL }, }; u_int debug; size_t maxpathlen; struct timespec start_time; static fstype_t *get_fstype(const char *); static void usage(void) __dead; int main(int, char *[]); int main(int argc, char *argv[]) { struct timeval start; fstype_t *fstype; fsinfo_t fsoptions; fsnode *root; int ch, len; char *specfile; #ifdef MAXPATHLEN maxpathlen = MAXPATHLEN; #elif !defined(_PC_PATH_MAX) maxpathlen = 1024; #else { long r; if ((r = sysconf(_PC_PATH_MAX)) == -1) err(1, "sysconf(_PC_PATH_MAX) == -1"); maxpathlen = r; } #endif #ifdef __NetBSD__ setprogname(argv[0]); #endif debug = 0; if ((fstype = get_fstype(DEFAULT_FSTYPE)) == NULL) errx(1, "Unknown default fs type `%s'.", DEFAULT_FSTYPE); /* set default fsoptions */ (void)memset(&fsoptions, 0, sizeof(fsoptions)); fsoptions.fd = -1; fsoptions.sectorsize = -1; fsoptions.maxtime = -1; if (fstype->prepare_options) fstype->prepare_options(&fsoptions); specfile = NULL; if (gettimeofday(&start, NULL) == -1) err(1, "Unable to get system time"); start_time.tv_sec = start.tv_sec; start_time.tv_nsec = start.tv_usec * 1000; while ((ch = getopt(argc, argv, "B:b:d:f:F:M:m:N:o:s:S:t:T:x")) != -1) { switch (ch) { case 'B': if (strcmp(optarg, "be") == 0 || strcmp(optarg, "4321") == 0 || strcmp(optarg, "big") == 0) { #if BYTE_ORDER == LITTLE_ENDIAN fsoptions.needswap = 1; #endif } else if (strcmp(optarg, "le") == 0 || strcmp(optarg, "1234") == 0 || strcmp(optarg, "little") == 0) { #if BYTE_ORDER == BIG_ENDIAN fsoptions.needswap = 1; #endif } else { warnx("Invalid endian `%s'.", optarg); usage(); } break; case 'b': len = strlen(optarg) - 1; if (optarg[len] == '%') { optarg[len] = '\0'; fsoptions.freeblockpc = strsuftoll("free block percentage", optarg, 0, 99); } else { fsoptions.freeblocks = strsuftoll("free blocks", optarg, 0, LLONG_MAX); } break; case 'd': debug = strtoll(optarg, NULL, 0); break; case 'f': len = strlen(optarg) - 1; if (optarg[len] == '%') { optarg[len] = '\0'; fsoptions.freefilepc = strsuftoll("free file percentage", optarg, 0, 99); } else { fsoptions.freefiles = strsuftoll("free files", optarg, 0, LLONG_MAX); } break; case 'F': specfile = optarg; break; case 'M': fsoptions.minsize = strsuftoll("minimum size", optarg, 1LL, LLONG_MAX); break; case 'N': if (! setup_getid(optarg)) errx(1, "Unable to use user and group databases in `%s'", optarg); break; case 'm': fsoptions.maxsize = strsuftoll("maximum size", optarg, 1LL, LLONG_MAX); break; case 'o': { char *p; while ((p = strsep(&optarg, ",")) != NULL) { if (*p == '\0') errx(1, "Empty option"); if (! fstype->parse_options(p, &fsoptions)) usage(); } break; } case 's': fsoptions.minsize = fsoptions.maxsize = strsuftoll("size", optarg, 1LL, LLONG_MAX); break; case 'S': fsoptions.sectorsize = (int)strsuftoll("sector size", optarg, 1LL, INT_MAX); break; case 't': /* Check current one and cleanup if necessary. */ if (fstype->cleanup_options) fstype->cleanup_options(&fsoptions); fsoptions.fs_specific = NULL; if ((fstype = get_fstype(optarg)) == NULL) errx(1, "Unknown fs type `%s'.", optarg); fstype->prepare_options(&fsoptions); break; case 'T': fsoptions.maxtime = strtoll(optarg, NULL, 10); if (start_time.tv_sec >= fsoptions.maxtime) { start_time.tv_sec = fsoptions.maxtime; start_time.tv_nsec = 0; } break; case 'x': fsoptions.onlyspec = 1; break; case '?': default: usage(); /* NOTREACHED */ } } if (debug) { printf("debug mask: 0x%08x\n", debug); printf("start time: %ld.%ld, %s", (long)start_time.tv_sec, (long)start_time.tv_nsec, ctime(&start_time.tv_sec)); } argc -= optind; argv += optind; if (argc != 2) usage(); /* -x must be accompanied by -F */ if (fsoptions.onlyspec != 0 && specfile == NULL) errx(1, "-x requires -F mtree-specfile."); /* walk the tree */ TIMER_START(start); root = walk_dir(argv[1], NULL); TIMER_RESULTS(start, "walk_dir"); if (specfile) { /* apply a specfile */ TIMER_START(start); apply_specfile(specfile, argv[1], root, fsoptions.onlyspec); TIMER_RESULTS(start, "apply_specfile"); } if (debug & DEBUG_DUMP_FSNODES) { printf("\nparent: %s\n", argv[1]); dump_fsnodes(".", root); putchar('\n'); } /* build the filesystem */ TIMER_START(start); fstype->make_fs(argv[0], argv[1], root, &fsoptions); TIMER_RESULTS(start, "make_fs"); free_fsnodes(root); exit(0); /* NOTREACHED */ } int set_option(option_t *options, const char *var, const char *val) { int i; for (i = 0; options[i].name != NULL; i++) { if (strcmp(options[i].name, var) != 0) continue; *options[i].value = (int)strsuftoll(options[i].desc, val, options[i].minimum, options[i].maximum); return (1); } warnx("Unknown option `%s'", var); return (0); } static fstype_t * get_fstype(const char *type) { int i; for (i = 0; fstypes[i].type != NULL; i++) if (strcmp(fstypes[i].type, type) == 0) return (&fstypes[i]); return (NULL); } static void usage(void) { #ifdef __NetBSD__ const char *prog; prog = getprogname(); #define __progname prog #else extern const char *__progname; #endif fprintf(stderr, "usage: %s [-t fs-type] [-o fs-options] [-d debug-mask] [-B endian]\n" "\t[-S sector-size] [-M minimum-size] [-m maximum-size] [-s image-size]\n" "\t[-b free-blocks] [-f free-files] [-F mtree-specfile] [-x]\n" "\t[-N userdb-dir] [-T maximum-time] image-file directory\n", __progname); exit(1); } makefs/src/usr.sbin/makefs/makefs.h010064400000000000000000000213121341415427300144750ustar00/** $MirOS: src/usr.sbin/makefs/makefs.h,v 1.20 2019/01/05 16:17:36 tg Exp $ */ /* $NetBSD: makefs.h,v 1.20 2008/12/28 21:51:46 christos Exp $ */ /* * Copyright (c) 2009, 2010, 2013 * Thorsten Glaser * Copyright (c) 2001 Wasabi Systems, Inc. * All rights reserved. * * Written by Luke Mewburn 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 _MAKEFS_H #define _MAKEFS_H #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #else #ifndef HAVE_STRUCT_STAT_ST_FLAGS #define HAVE_STRUCT_STAT_ST_FLAGS 1 #endif #ifndef HAVE_STRUCT_STAT_ST_GEN #define HAVE_STRUCT_STAT_ST_GEN 1 #endif #ifndef HAVE_STRUCT_STAT_ST_MTIMENSEC #define HAVE_STRUCT_STAT_ST_MTIMENSEC 1 #endif #ifndef HAVE_STRUCT_STATVFS_F_IOSIZE #define HAVE_STRUCT_STATVFS_F_IOSIZE 1 #endif #ifndef HAVE_STRUCT_STAT_BIRTHTIME #define HAVE_STRUCT_STAT_BIRTHTIME 1 #endif #ifndef HAVE_FSTATVFS #define HAVE_FSTATVFS 1 #endif #endif #include #include #include #ifdef GNUPORT #include #undef __unused #define __unused __attribute__((__unused__)) extern size_t strlcat(char *, const char *, size_t); extern size_t strlcpy(char *, const char *, size_t); #endif /* * fsnode - * a component of the tree; contains a filename, a pointer to * fsinode, optional symlink name, and tree pointers * * fsinode - * equivalent to an inode, containing serial number (sort of virtual * fs independent inode number) target filesystem inode number (file * data sector in the cd9660 case), refcount (nlink), and stat buffer * * A tree of fsnodes looks like this: * * name "." "bin" "netbsd" * type S_IFDIR S_IFDIR S_IFREG * next > > NULL * parent NULL NULL NULL * child NULL v * * name "." "ls" * type S_IFDIR S_IFREG * next > NULL * parent ^ ^ (to "bin") * child NULL NULL * * Notes: * - first always points to first entry, at current level, which * must be "." when the tree has been built; during build it may * not be if "." hasn't yet been found by readdir(2). * * - serno is the same for a directory and its dot entry, but they * are not hardlinked to each other because the ffs code doesn't * like that; otherwise, they are unique and mostly sequential, * and the filesystems' code does the right thing anyway */ enum fi_flags { FI_SIZED = 1<<0, /* inode sized */ FI_ALLOCATED = 1<<1, /* fsinode->ino allocated */ FI_WRITTEN = 1<<2, /* inode written */ }; typedef struct { uint32_t serno; /* serial number / virtual inode no. */ uint32_t ino; /* inode number used on target fs */ uint32_t nlink; /* number of links to this entry */ enum fi_flags flags; /* flags used by fs specific code */ struct stat st; /* stat entry */ } fsinode; typedef struct _fsnode { struct _fsnode *parent; /* parent (NULL if root) */ struct _fsnode *child; /* child (if type == S_IFDIR) */ struct _fsnode *next; /* next */ struct _fsnode *first; /* first node of current level (".") */ uint32_t type; /* type of entry */ fsinode *inode; /* actual inode data */ char *symlink; /* symlink target */ char *name; /* file name */ int flags; /* misc flags */ } fsnode; #define FSNODE_F_HASSPEC 0x01 /* fsnode has a spec entry */ /* * fsinfo_t - contains various settings and parameters pertaining to * the image, including current settings, global options, and fs * specific options */ typedef struct { /* current settings */ off_t size; /* total size */ off_t inodes; /* number of inodes */ uint32_t curinode; /* current inode */ /* image settings */ int fd; /* file descriptor of image */ void *superblock; /* superblock */ int onlyspec; /* only add entries in specfile */ /* global options */ off_t minsize; /* minimum size image should be */ off_t maxsize; /* maximum size image can be */ off_t freefiles; /* free file entries to leave */ int freefilepc; /* free file % */ off_t freeblocks; /* free blocks to leave */ int freeblockpc; /* free block % */ int needswap; /* non-zero if byte swapping needed */ int sectorsize; /* sector size */ time_t maxtime; /* maximum allowed timestamp value */ void *fs_specific; /* Filesystem specific additions. */ } fsinfo_t; /* * option_t - contains option name, description, pointer to location to store * result, and range checks for the result. Used to simplify fs specific * option setting */ typedef struct { const char *name; /* option name */ int *value; /* where to stuff the value */ int minimum; /* minimum for value */ int maximum; /* maximum for value */ const char *desc; /* option description */ } option_t; void apply_specfile(const char *, const char *, fsnode *, int); void dump_fsnodes(const char *, fsnode *); const char * inode_type(mode_t); int set_option(option_t *, const char *, const char *); fsnode * walk_dir(const char *, fsnode *); void free_fsnodes(fsnode *); void ffs_prep_opts(fsinfo_t *); int ffs_parse_opts(const char *, fsinfo_t *); void ffs_cleanup_opts(fsinfo_t *); void ffs_makefs(const char *, const char *, fsnode *, fsinfo_t *); void cd9660_prep_opts(fsinfo_t *); int cd9660_parse_opts(const char *, fsinfo_t *); void cd9660_cleanup_opts(fsinfo_t *); void cd9660_makefs(const char *, const char *, fsnode *, fsinfo_t *); extern u_int debug; extern size_t maxpathlen; extern struct timespec start_time; /* * If -x is specified, we want to exclude nodes which do not appear * in the spec file. */ #define FSNODE_EXCLUDE_P(opts, fsnode) \ ((opts)->onlyspec != 0 && ((fsnode)->flags & FSNODE_F_HASSPEC) == 0) #define DEBUG_TIME 0x00000001 /* debug bits 1..3 unused at this time */ #define DEBUG_WALK_DIR 0x00000010 #define DEBUG_WALK_DIR_NODE 0x00000020 #define DEBUG_WALK_DIR_LINKCHECK 0x00000040 #define DEBUG_DUMP_FSNODES 0x00000080 #define DEBUG_DUMP_FSNODES_VERBOSE 0x00000100 #define DEBUG_FS_PARSE_OPTS 0x00000200 #define DEBUG_FS_MAKEFS 0x00000400 #define DEBUG_FS_VALIDATE 0x00000800 #define DEBUG_FS_CREATE_IMAGE 0x00001000 #define DEBUG_FS_SIZE_DIR 0x00002000 #define DEBUG_FS_SIZE_DIR_NODE 0x00004000 #define DEBUG_FS_SIZE_DIR_ADD_DIRENT 0x00008000 #define DEBUG_FS_POPULATE 0x00010000 #define DEBUG_FS_POPULATE_DIRBUF 0x00020000 #define DEBUG_FS_POPULATE_NODE 0x00040000 #define DEBUG_FS_WRITE_FILE 0x00080000 #define DEBUG_FS_WRITE_FILE_BLOCK 0x00100000 #define DEBUG_FS_MAKE_DIRBUF 0x00200000 #define DEBUG_FS_WRITE_INODE 0x00400000 #define DEBUG_BUF_BREAD 0x00800000 #define DEBUG_BUF_BWRITE 0x01000000 #define DEBUG_BUF_GETBLK 0x02000000 #define DEBUG_APPLY_SPECFILE 0x04000000 #define DEBUG_APPLY_SPECENTRY 0x08000000 #define DEBUG_APPLY_SPECONLY 0x10000000 #define TIMER_START(x) \ if (debug & DEBUG_TIME) \ gettimeofday(&(x), NULL) #define TIMER_RESULTS(x,d) \ if (debug & DEBUG_TIME) { \ struct timeval end, td; \ gettimeofday(&end, NULL); \ timersub(&end, &(x), &td); \ printf("%s took %lld.%06ld seconds\n", \ (d), (long long)td.tv_sec, \ (long)td.tv_usec); \ } #ifndef DEFAULT_FSTYPE #define DEFAULT_FSTYPE "ffs" #endif /* * ffs specific settings * --------------------- */ #define FFS_EI /* for opposite endian support in ffs headers */ #endif /* _MAKEFS_H */ makefs/src/usr.sbin/makefs/mbsdtree.h010064400000000000000000000054161341415245600150440ustar00/* $MirOS: src/usr.sbin/makefs/mbsdtree.h,v 1.6 2019/01/05 16:02:27 tg Exp $ */ /* $OpenBSD: util.h,v 1.26 2004/07/13 21:09:48 millert Exp $ */ /* $NetBSD: util.h,v 1.2 1996/05/16 07:00:22 thorpej Exp $ */ /*- * Copyright (c) 2009, 2010, 2013 * Thorsten Glaser * Copyright (c) 1995 * The Regents of the University of California. All rights reserved. * Portions Copyright (c) 1996, Jason Downs. 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 MBSDTREE_H #define MBSDTREE_H #include __BEGIN_DECLS /* lib/libc/stdlib/strsuftoll.c */ long long strsuftoll(const char *, const char *, long long, long long); long long strsuftollx(const char *, const char *, long long, long long, char *, size_t) #if defined(__GNUC__) && (defined(__OpenBSD__) || defined(__MirBSD__)) __attribute__((__bounded__(__string__, 5, 6))) #endif ; #ifndef GNUPORT /* lib/libutil/stat_flags.c */ char *flags_to_string(u_long, const char *); int string_to_flags(char **, u_long *, u_long *); #endif #ifdef NEED_FPARSELN_DECL /* * fparseln() specific operation flags. */ #define FPARSELN_UNESCESC 0x01 #define FPARSELN_UNESCCONT 0x02 #define FPARSELN_UNESCCOMM 0x04 #define FPARSELN_UNESCREST 0x08 #define FPARSELN_UNESCALL 0x0f char *fparseln(FILE *, size_t *, size_t *, const char[3], int); #endif __END_DECLS #endif makefs/src/usr.sbin/makefs/nbsrc/lib/libc/gen/pwcache.c010064400000000000000000000377231341415427400202110ustar00/** $MirOS: src/usr.sbin/makefs/nbsrc/lib/libc/gen/pwcache.c,v 1.9 2019/01/05 16:17:37 tg Exp $ */ /* $NetBSD: pwcache.c,v 1.30 2008/04/28 20:22:59 martin Exp $ */ /*- * Copyright (c) 2009 * Thorsten Glaser * Copyright (c) 1992 Keith Muller. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Keith Muller of the University of California, San Diego. * * 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. */ /*- * Copyright (c) 2002 The NetBSD Foundation, Inc. * 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 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. */ #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" /* * XXX Undefine the renames of these functions so that we don't * XXX rename the versions found in the host's by mistake! */ #undef group_from_gid #undef user_from_uid #endif #include #if defined(LIBC_SCCS) && !defined(lint) #if 0 static char sccsid[] = "@(#)cache.c 8.1 (Berkeley) 5/31/93"; #else __RCSID("$NetBSD: pwcache.c,v 1.30 2008/04/28 20:22:59 martin Exp $"); __IDSTRING(mbsdid, "$MirOS: src/usr.sbin/makefs/nbsrc/lib/libc/gen/pwcache.c,v 1.9 2019/01/05 16:17:37 tg Exp $"); #endif #endif /* LIBC_SCCS and not lint */ #ifdef _LIBC #include "namespace.h" #endif #include #include #include #include #include #include #include #include #include #if HAVE_NBTOOL_CONFIG_H || defined(__MirBSD__) || defined(GNUPORT) /* XXX Now, re-apply the renaming that we undid above. */ #define group_from_gid __nbcompat_group_from_gid #define user_from_uid __nbcompat_user_from_uid #endif #if defined(__weak_alias) && !defined(__MirBSD__) && !defined(GNUPORT) __weak_alias(user_from_uid,_user_from_uid) __weak_alias(group_from_gid,_group_from_gid) __weak_alias(pwcache_userdb,_pwcache_userdb) __weak_alias(pwcache_groupdb,_pwcache_groupdb) #endif #ifdef GNUPORT #define setgroupent NULL #define setpassent NULL extern size_t strlcpy(char *, const char *, size_t); #endif #if !HAVE_PWCACHE_USERDB || HAVE_NBTOOL_CONFIG_H #include "pwcache.h" /* * routines that control user, group, uid and gid caches (for the archive * member print routine). * IMPORTANT: * these routines cache BOTH hits and misses, a major performance improvement */ /* * function pointers to various name lookup routines. * these may be changed as necessary. */ static int (*_pwcache_setgroupent)(int) = setgroupent; static void (*_pwcache_endgrent)(void) = endgrent; static struct group * (*_pwcache_getgrnam)(const char *) = getgrnam; static struct group * (*_pwcache_getgrgid)(gid_t) = getgrgid; static int (*_pwcache_setpassent)(int) = setpassent; static void (*_pwcache_endpwent)(void) = endpwent; static struct passwd * (*_pwcache_getpwnam)(const char *) = getpwnam; static struct passwd * (*_pwcache_getpwuid)(uid_t) = getpwuid; /* * internal state */ static int pwopn; /* is password file open */ static int gropn; /* is group file open */ static UIDC **uidtb; /* uid to name cache */ static GIDC **gidtb; /* gid to name cache */ static UIDC **usrtb; /* user name to uid cache */ static GIDC **grptb; /* group name to gid cache */ static int uidtb_fail; /* uidtb_start() failed ? */ static int gidtb_fail; /* gidtb_start() failed ? */ static int usrtb_fail; /* usrtb_start() failed ? */ static int grptb_fail; /* grptb_start() failed ? */ static u_int st_hash(const char *, size_t, int); static int uidtb_start(void); static int gidtb_start(void); static int usrtb_start(void); static int grptb_start(void); static u_int st_hash(const char *name, size_t len, int tabsz) { u_int key = 0; _DIAGASSERT(name != NULL); while (len--) { key += *name++; key = (key << 8) | (key >> 24); } return (key % tabsz); } /* * uidtb_start * creates an an empty uidtb * Return: * 0 if ok, -1 otherwise */ static int uidtb_start(void) { if (uidtb != NULL) return (0); if (uidtb_fail) return (-1); if ((uidtb = (UIDC **)calloc(UID_SZ, sizeof(UIDC *))) == NULL) { ++uidtb_fail; return (-1); } return (0); } /* * gidtb_start * creates an an empty gidtb * Return: * 0 if ok, -1 otherwise */ static int gidtb_start(void) { if (gidtb != NULL) return (0); if (gidtb_fail) return (-1); if ((gidtb = (GIDC **)calloc(GID_SZ, sizeof(GIDC *))) == NULL) { ++gidtb_fail; return (-1); } return (0); } /* * usrtb_start * creates an an empty usrtb * Return: * 0 if ok, -1 otherwise */ static int usrtb_start(void) { if (usrtb != NULL) return (0); if (usrtb_fail) return (-1); if ((usrtb = (UIDC **)calloc(UNM_SZ, sizeof(UIDC *))) == NULL) { ++usrtb_fail; return (-1); } return (0); } /* * grptb_start * creates an an empty grptb * Return: * 0 if ok, -1 otherwise */ static int grptb_start(void) { if (grptb != NULL) return (0); if (grptb_fail) return (-1); if ((grptb = (GIDC **)calloc(GNM_SZ, sizeof(GIDC *))) == NULL) { ++grptb_fail; return (-1); } return (0); } /* * user_from_uid() * caches the name (if any) for the uid. If noname clear, we always * return the stored name (if valid or invalid match). * We use a simple hash table. * Return * Pointer to stored name (or a empty string) */ const char * user_from_uid(uid_t uid, int noname) { struct passwd *pw; UIDC *ptr, **pptr; if ((uidtb == NULL) && (uidtb_start() < 0)) return (NULL); /* * see if we have this uid cached */ pptr = uidtb + (uid % UID_SZ); ptr = *pptr; if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) { /* * have an entry for this uid */ if (!noname || (ptr->valid == VALID)) return (ptr->name); return (NULL); } /* * No entry for this uid, we will add it */ if (!pwopn) { if (_pwcache_setpassent != NULL) (*_pwcache_setpassent)(1); ++pwopn; } if (ptr == NULL) *pptr = ptr = (UIDC *)malloc(sizeof(UIDC)); if ((pw = (*_pwcache_getpwuid)(uid)) == NULL) { /* * no match for this uid in the local password file * a string that is the uid in numeric format */ if (ptr == NULL) return (NULL); ptr->uid = uid; (void)snprintf(ptr->name, UNMLEN, "%lu", (long) uid); ptr->valid = INVALID; if (noname) return (NULL); } else { /* * there is an entry for this uid in the password file */ if (ptr == NULL) return (pw->pw_name); ptr->uid = uid; (void)strlcpy(ptr->name, pw->pw_name, UNMLEN); ptr->valid = VALID; } return (ptr->name); } /* * group_from_gid() * caches the name (if any) for the gid. If noname clear, we always * return the stored name (if valid or invalid match). * We use a simple hash table. * Return * Pointer to stored name (or a empty string) */ const char * group_from_gid(gid_t gid, int noname) { struct group *gr; GIDC *ptr, **pptr; if ((gidtb == NULL) && (gidtb_start() < 0)) return (NULL); /* * see if we have this gid cached */ pptr = gidtb + (gid % GID_SZ); ptr = *pptr; if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) { /* * have an entry for this gid */ if (!noname || (ptr->valid == VALID)) return (ptr->name); return (NULL); } /* * No entry for this gid, we will add it */ if (!gropn) { if (_pwcache_setgroupent != NULL) (*_pwcache_setgroupent)(1); ++gropn; } if (ptr == NULL) *pptr = ptr = (GIDC *)malloc(sizeof(GIDC)); if ((gr = (*_pwcache_getgrgid)(gid)) == NULL) { /* * no match for this gid in the local group file, put in * a string that is the gid in numberic format */ if (ptr == NULL) return (NULL); ptr->gid = gid; (void)snprintf(ptr->name, GNMLEN, "%lu", (long) gid); ptr->valid = INVALID; if (noname) return (NULL); } else { /* * there is an entry for this group in the group file */ if (ptr == NULL) return (gr->gr_name); ptr->gid = gid; (void)strlcpy(ptr->name, gr->gr_name, GNMLEN); ptr->valid = VALID; } return (ptr->name); } /* * uid_from_user() * caches the uid for a given user name. We use a simple hash table. * Return * the uid (if any) for a user name, or a -1 if no match can be found */ int uid_from_user(const char *name, uid_t *uid) { struct passwd *pw; UIDC *ptr, **pptr; size_t namelen; /* * return -1 for mangled names */ if (name == NULL || ((namelen = strlen(name)) == 0)) return (-1); if ((usrtb == NULL) && (usrtb_start() < 0)) return (-1); /* * look up in hash table, if found and valid return the uid, * if found and invalid, return a -1 */ pptr = usrtb + st_hash(name, namelen, UNM_SZ); ptr = *pptr; if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) { if (ptr->valid == INVALID) return (-1); *uid = ptr->uid; return (0); } if (!pwopn) { if (_pwcache_setpassent != NULL) (*_pwcache_setpassent)(1); ++pwopn; } if (ptr == NULL) *pptr = ptr = (UIDC *)malloc(sizeof(UIDC)); /* * no match, look it up, if no match store it as an invalid entry, * or store the matching uid */ if (ptr == NULL) { if ((pw = (*_pwcache_getpwnam)(name)) == NULL) return (-1); *uid = pw->pw_uid; return (0); } (void)strlcpy(ptr->name, name, UNMLEN); if ((pw = (*_pwcache_getpwnam)(name)) == NULL) { ptr->valid = INVALID; return (-1); } ptr->valid = VALID; *uid = ptr->uid = pw->pw_uid; return (0); } /* * gid_from_group() * caches the gid for a given group name. We use a simple hash table. * Return * the gid (if any) for a group name, or a -1 if no match can be found */ int gid_from_group(const char *name, gid_t *gid) { struct group *gr; GIDC *ptr, **pptr; size_t namelen; /* * return -1 for mangled names */ if (name == NULL || ((namelen = strlen(name)) == 0)) return (-1); if ((grptb == NULL) && (grptb_start() < 0)) return (-1); /* * look up in hash table, if found and valid return the uid, * if found and invalid, return a -1 */ pptr = grptb + st_hash(name, namelen, GID_SZ); ptr = *pptr; if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) { if (ptr->valid == INVALID) return (-1); *gid = ptr->gid; return (0); } if (!gropn) { if (_pwcache_setgroupent != NULL) (*_pwcache_setgroupent)(1); ++gropn; } if (ptr == NULL) *pptr = ptr = (GIDC *)malloc(sizeof(GIDC)); /* * no match, look it up, if no match store it as an invalid entry, * or store the matching gid */ if (ptr == NULL) { if ((gr = (*_pwcache_getgrnam)(name)) == NULL) return (-1); *gid = gr->gr_gid; return (0); } (void)strlcpy(ptr->name, name, GNMLEN); if ((gr = (*_pwcache_getgrnam)(name)) == NULL) { ptr->valid = INVALID; return (-1); } ptr->valid = VALID; *gid = ptr->gid = gr->gr_gid; return (0); } #define FLUSHTB(arr, len, fail) \ do { \ if (arr != NULL) { \ for (i = 0; i < len; i++) \ if (arr[i] != NULL) \ free(arr[i]); \ arr = NULL; \ } \ fail = 0; \ } while (/* CONSTCOND */0); int pwcache_userdb( int (*a_setpassent)(int), void (*a_endpwent)(void), struct passwd * (*a_getpwnam)(const char *), struct passwd * (*a_getpwuid)(uid_t)) { int i; /* a_setpassent and a_endpwent may be NULL */ if (a_getpwnam == NULL || a_getpwuid == NULL) return (-1); if (_pwcache_endpwent != NULL) (*_pwcache_endpwent)(); FLUSHTB(uidtb, UID_SZ, uidtb_fail); FLUSHTB(usrtb, UNM_SZ, usrtb_fail); pwopn = 0; _pwcache_setpassent = a_setpassent; _pwcache_endpwent = a_endpwent; _pwcache_getpwnam = a_getpwnam; _pwcache_getpwuid = a_getpwuid; return (0); } int pwcache_groupdb( int (*a_setgroupent)(int), void (*a_endgrent)(void), struct group * (*a_getgrnam)(const char *), struct group * (*a_getgrgid)(gid_t)) { int i; /* a_setgroupent and a_endgrent may be NULL */ if (a_getgrnam == NULL || a_getgrgid == NULL) return (-1); if (_pwcache_endgrent != NULL) (*_pwcache_endgrent)(); FLUSHTB(gidtb, GID_SZ, gidtb_fail); FLUSHTB(grptb, GNM_SZ, grptb_fail); gropn = 0; _pwcache_setgroupent = a_setgroupent; _pwcache_endgrent = a_endgrent; _pwcache_getgrnam = a_getgrnam; _pwcache_getgrgid = a_getgrgid; return (0); } #ifdef TEST_PWCACHE struct passwd * test_getpwnam(const char *name) { static struct passwd foo; memset(&foo, 0, sizeof(foo)); if (strcmp(name, "toor") == 0) { foo.pw_uid = 666; return &foo; } return (getpwnam(name)); } int main(int argc, char *argv[]) { uid_t u; int r, i; printf("pass 1 (default userdb)\n"); for (i = 1; i < argc; i++) { printf("i: %d, pwopn %d usrtb_fail %d usrtb %p\n", i, pwopn, usrtb_fail, usrtb); r = uid_from_user(argv[i], &u); if (r == -1) printf(" uid_from_user %s: failed\n", argv[i]); else printf(" uid_from_user %s: %d\n", argv[i], u); } printf("pass 1 finish: pwopn %d usrtb_fail %d usrtb %p\n", pwopn, usrtb_fail, usrtb); puts(""); printf("pass 2 (replacement userdb)\n"); printf("pwcache_userdb returned %d\n", pwcache_userdb(setpassent, test_getpwnam, getpwuid)); printf("pwopn %d usrtb_fail %d usrtb %p\n", pwopn, usrtb_fail, usrtb); for (i = 1; i < argc; i++) { printf("i: %d, pwopn %d usrtb_fail %d usrtb %p\n", i, pwopn, usrtb_fail, usrtb); u = -1; r = uid_from_user(argv[i], &u); if (r == -1) printf(" uid_from_user %s: failed\n", argv[i]); else printf(" uid_from_user %s: %d\n", argv[i], u); } printf("pass 2 finish: pwopn %d usrtb_fail %d usrtb %p\n", pwopn, usrtb_fail, usrtb); puts(""); printf("pass 3 (null pointers)\n"); printf("pwcache_userdb returned %d\n", pwcache_userdb(NULL, NULL, NULL)); return (0); } #endif /* TEST_PWCACHE */ #endif /* !HAVE_PWCACHE_USERDB */ makefs/src/usr.sbin/makefs/nbsrc/lib/libc/gen/pwcache.h010064400000000000000000000100161341415245700202010ustar00/** $MirOS: src/usr.sbin/makefs/nbsrc/lib/libc/gen/pwcache.h,v 1.11 2019/01/05 16:02:28 tg Exp $ */ /* $NetBSD: pwcache.h,v 1.5 2003/11/10 08:51:51 wiz Exp $ */ /*- * Copyright (c) 2009 * Thorsten Glaser * Copyright (c) 1992 Keith Muller. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Keith Muller of the University of California, San Diego. * * 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. * * @(#)cache.h 8.1 (Berkeley) 5/31/93 */ /* * Constants and data structures used to implement group and password file * caches. Traditional passwd/group cache routines perform quite poorly with * archives. The chances of hitting a valid lookup with an archive is quite a * bit worse than with files already resident on the filesystem. These misses * create a MAJOR performance cost. To address this problem, these routines * cache both hits and misses. * * NOTE: name lengths must be as large as those stored in ANY PROTOCOL and * as stored in the passwd and group files. CACHE SIZES MUST BE PRIME */ #define UNMLEN 32 /* >= user name found in any protocol */ #define GNMLEN 32 /* >= group name found in any protocol */ #define UID_SZ 317 /* size of uid to user_name cache */ #define UNM_SZ 317 /* size of user_name to uid cache */ #define GID_SZ 251 /* size of gid to group_name cache */ #define GNM_SZ 251 /* size of group_name to gid cache */ #define VALID 1 /* entry and name are valid */ #define INVALID 2 /* entry valid, name NOT valid */ /* * Node structures used in the user, group, uid, and gid caches. */ typedef struct uidc { int valid; /* is this a valid or a miss entry */ char name[UNMLEN]; /* uid name */ uid_t uid; /* cached uid */ } UIDC; typedef struct gidc { int valid; /* is this a valid or a miss entry */ char name[GNMLEN]; /* gid name */ gid_t gid; /* cached gid */ } GIDC; #if defined(__MirBSD__) || defined(GNUPORT) const char *__nbcompat_user_from_uid(uid_t uid, int noname); const char *__nbcompat_group_from_gid(gid_t gid, int noname); int uid_from_user(const char *name, uid_t *uid); int gid_from_group(const char *name, gid_t *gid); int pwcache_userdb( int (*a_setpassent)(int), void (*a_endpwent)(void), struct passwd * (*a_getpwnam)(const char *), struct passwd * (*a_getpwuid)(uid_t)); int pwcache_groupdb( int (*a_setgroupent)(int), void (*a_endgrent)(void), struct group * (*a_getgrnam)(const char *), struct group * (*a_getgrgid)(gid_t)); #define user_from_uid __nbcompat_user_from_uid #define group_from_gid __nbcompat_group_from_gid #endif makefs/src/usr.sbin/makefs/nbsrc/lib/libc/stdlib/strsuftoll.c010064400000000000000000000166111341415245700215220ustar00/** $MirOS: src/usr.sbin/makefs/nbsrc/lib/libc/stdlib/strsuftoll.c,v 1.11 2019/01/05 16:02:28 tg Exp $ */ /* $NetBSD: strsuftoll.c,v 1.8 2008/04/28 20:23:00 martin Exp $ */ /*- * Copyright (c) 2009 * Thorsten Glaser * Copyright (c) 2001-2002,2004 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. */ /*- * Copyright (c) 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Keith Muller of the University of California, San Diego and Lance * Visser of Convex Computer Corporation. * * 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 HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #if defined(__MirBSD__) || defined(GNUPORT) #include "mbsdtree.h" #endif #include #if defined(LIBC_SCCS) && !defined(lint) __RCSID("$NetBSD: strsuftoll.c,v 1.8 2008/04/28 20:23:00 martin Exp $"); __IDSTRING(mbsdid, "$MirOS: src/usr.sbin/makefs/nbsrc/lib/libc/stdlib/strsuftoll.c,v 1.11 2019/01/05 16:02:28 tg Exp $"); #endif /* LIBC_SCCS and not lint */ #ifdef _LIBC #include "namespace.h" #endif #if !HAVE_STRSUFTOLL #include #include #include #include #include #include #include #include #include #include #ifdef _LIBC # ifdef __weak_alias __weak_alias(strsuftoll, _strsuftoll) __weak_alias(strsuftollx, _strsuftollx) # endif #endif /* LIBC */ /* * Convert an expression of the following forms to a (u)int64_t. * 1) A positive decimal number. * 2) A positive decimal number followed by a b (mult by 512). * 3) A positive decimal number followed by a k (mult by 1024). * 4) A positive decimal number followed by a m (mult by 1048576). * 5) A positive decimal number followed by a g (mult by 1073741824). * 6) A positive decimal number followed by a t (mult by 1099511627776). * 7) A positive decimal number followed by a w (mult by sizeof int) * 8) Two or more positive decimal numbers (with/without k,b or w). * separated by x (also * for backwards compatibility), specifying * the product of the indicated values. * Returns the result upon successful conversion, or exits with an * appropriate error. * */ /* LONGLONG */ long long strsuftoll(const char *desc, const char *val, long long min, long long max) { long long result; char errbuf[100]; result = strsuftollx(desc, val, min, max, errbuf, sizeof(errbuf)); if (*errbuf != '\0') errx(1, "%s", errbuf); return (result); } /* * As strsuftoll(), but returns the error message into the provided buffer * rather than exiting with it. */ /* LONGLONG */ long long strsuftollx(const char *desc, const char *val, long long min, long long max, char *ebuf, size_t ebuflen) { long long num, t; char *expr; _DIAGASSERT(desc != NULL); _DIAGASSERT(val != NULL); _DIAGASSERT(ebuf != NULL); errno = 0; ebuf[0] = '\0'; while (isspace((unsigned char)*val)) /* Skip leading space */ val++; if (*val == '0') num = strtoll(val, &expr, 0); else num = strtoll(val, &expr, 10); if (errno == ERANGE) goto erange; /* Overflow */ if (expr == val) /* No digits */ goto badnum; switch (*expr) { case 'b': t = num; num *= 512; /* 1 block */ if (t > num) goto erange; ++expr; break; case 'k': t = num; num *= 1024; /* 1 kibibyte */ if (t > num) goto erange; ++expr; break; case 'm': t = num; num *= 1048576; /* 1 mebibyte */ if (t > num) goto erange; ++expr; break; case 'g': t = num; num *= 1073741824; /* 1 gibibyte */ if (t > num) goto erange; ++expr; break; case 't': t = num; num *= 1099511627776LL; /* 1 tebibyte */ if (t > num) goto erange; ++expr; break; case 'w': t = num; num *= sizeof(int); /* 1 word */ if (t > num) goto erange; ++expr; break; } switch (*expr) { case '\0': break; case '*': /* Backward compatible */ case 'x': t = num; num *= strsuftollx(desc, expr + 1, min, max, ebuf, ebuflen); if (*ebuf != '\0') return (0); if (t > num) { erange: snprintf(ebuf, ebuflen, "%s: %s", desc, strerror(ERANGE)); return (0); } break; default: badnum: snprintf(ebuf, ebuflen, "%s `%s': illegal number", desc, val); return (0); } if (num < min) { /* LONGLONG */ snprintf(ebuf, ebuflen, "%s %lld is less than %lld.", desc, (long long)num, (long long)min); return (0); } if (num > max) { /* LONGLONG */ snprintf(ebuf, ebuflen, "%s %lld is greater than %lld.", desc, (long long)num, (long long)max); return (0); } *ebuf = '\0'; return (num); } #endif /* !HAVE_STRSUFTOLL */ makefs/src/usr.sbin/makefs/nbsrc/lib/libutil/stat_flags.c010064400000000000000000000117561110267364000207010ustar00/** $MirOS: src/usr.sbin/makefs/nbsrc/lib/libutil/stat_flags.c,v 1.2 2008/10/31 21:24:25 tg Exp $ */ /* $NetBSD: stat_flags.c,v 1.2 2007/01/16 17:34:02 cbiere Exp $ */ /*- * Copyright (c) 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. */ #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #else #define HAVE_STRUCT_STAT_ST_FLAGS 1 #endif #ifdef __MirBSD__ #include "mbsdtree.h" #endif #include #if !defined(lint) #if 0 static char sccsid[] = "@(#)stat_flags.c 8.2 (Berkeley) 7/28/94"; #else __RCSID("$NetBSD: stat_flags.c,v 1.2 2007/01/16 17:34:02 cbiere Exp $"); __IDSTRING(mbsdid, "$MirOS: src/usr.sbin/makefs/nbsrc/lib/libutil/stat_flags.c,v 1.2 2008/10/31 21:24:25 tg Exp $"); #endif #endif /* not lint */ #include #include #include #include #include #include #include "util.h" #define SAPPEND(s) do { \ if (prefix != NULL) \ (void)strlcat(string, prefix, sizeof(string)); \ (void)strlcat(string, s, sizeof(string)); \ prefix = ","; \ } while (/* CONSTCOND */ 0) /* * flags_to_string -- * Convert stat flags to a comma-separated string. If no flags * are set, return the default string. */ char * flags_to_string(u_long flags, const char *def) { char string[128]; const char *prefix; string[0] = '\0'; prefix = NULL; #if HAVE_STRUCT_STAT_ST_FLAGS if (flags & UF_APPEND) SAPPEND("uappnd"); if (flags & UF_IMMUTABLE) SAPPEND("uchg"); if (flags & UF_NODUMP) SAPPEND("nodump"); if (flags & UF_OPAQUE) SAPPEND("opaque"); if (flags & SF_APPEND) SAPPEND("sappnd"); if (flags & SF_ARCHIVED) SAPPEND("arch"); if (flags & SF_IMMUTABLE) SAPPEND("schg"); #ifdef SF_SNAPSHOT if (flags & SF_SNAPSHOT) SAPPEND("snap"); #endif #endif if (prefix != NULL) return strdup(string); return strdup(def); } #define TEST(a, b, f) { \ if (!strcmp(a, b)) { \ if (clear) { \ if (clrp) \ *clrp |= (f); \ if (setp) \ *setp &= ~(f); \ } else { \ if (setp) \ *setp |= (f); \ if (clrp) \ *clrp &= ~(f); \ } \ break; \ } \ } /* * string_to_flags -- * Take string of arguments and return stat flags. Return 0 on * success, 1 on failure. On failure, stringp is set to point * to the offending token. */ int string_to_flags(char **stringp, u_long *setp, u_long *clrp) { int clear; char *string, *p; if (setp) *setp = 0; if (clrp) *clrp = 0; #if HAVE_STRUCT_STAT_ST_FLAGS string = *stringp; while ((p = strsep(&string, "\t ,")) != NULL) { clear = 0; *stringp = p; if (*p == '\0') continue; if (p[0] == 'n' && p[1] == 'o') { clear = 1; p += 2; } switch (p[0]) { case 'a': TEST(p, "arch", SF_ARCHIVED); TEST(p, "archived", SF_ARCHIVED); return (1); case 'd': clear = !clear; TEST(p, "dump", UF_NODUMP); return (1); case 'n': /* * Support `nonodump'. Note that * the state of clear is not changed. */ TEST(p, "nodump", UF_NODUMP); return (1); case 'o': TEST(p, "opaque", UF_OPAQUE); return (1); case 's': TEST(p, "sappnd", SF_APPEND); TEST(p, "sappend", SF_APPEND); TEST(p, "schg", SF_IMMUTABLE); TEST(p, "schange", SF_IMMUTABLE); TEST(p, "simmutable", SF_IMMUTABLE); return (1); case 'u': TEST(p, "uappnd", UF_APPEND); TEST(p, "uappend", UF_APPEND); TEST(p, "uchg", UF_IMMUTABLE); TEST(p, "uchange", UF_IMMUTABLE); TEST(p, "uimmutable", UF_IMMUTABLE); return (1); default: return (1); } } #endif return (0); } makefs/src/usr.sbin/makefs/nbsrc/sbin/mknod/pack_dev.c010064400000000000000000000177431341414422100201550ustar00/* $NetBSD: pack_dev.c,v 1.10 2009/02/13 01:37:23 lukem Exp $ */ /*- * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Charles M. Hannum. * * 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. */ #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #include #if !defined(lint) __RCSID("$NetBSD: pack_dev.c,v 1.10 2009/02/13 01:37:23 lukem Exp $"); __IDSTRING(mbsdid, "$MirOS: src/usr.sbin/makefs/nbsrc/sbin/mknod/pack_dev.c,v 1.7 2019/01/05 15:08:38 tg Exp $"); #endif /* not lint */ #include #include #ifdef __GLIBC__ #include #endif #include #include #include #include #include #include #include "pack_dev.h" static pack_t pack_netbsd; static pack_t pack_freebsd; static pack_t pack_8_8; static pack_t pack_12_20; static pack_t pack_14_18; static pack_t pack_8_24; static pack_t pack_bsdos; static int compare_format(const void *, const void *); static const char iMajorError[] = "invalid major number"; static const char iMinorError[] = "invalid minor number"; static const char tooManyFields[] = "too many fields for format"; /* exported */ portdev_t pack_native(int n, u_long numbers[], const char **error) { portdev_t dev = 0; if (n == 2) { dev = makedev(numbers[0], numbers[1]); if ((u_long)major(dev) != numbers[0]) *error = iMajorError; else if ((u_long)minor(dev) != numbers[1]) *error = iMinorError; } else *error = tooManyFields; return (dev); } static portdev_t pack_netbsd(int n, u_long numbers[], const char **error) { portdev_t dev = 0; if (n == 2) { dev = makedev_netbsd(numbers[0], numbers[1]); if ((u_long)major_netbsd(dev) != numbers[0]) *error = iMajorError; else if ((u_long)minor_netbsd(dev) != numbers[1]) *error = iMinorError; } else *error = tooManyFields; return (dev); } #define major_freebsd(x) ((int32_t)(((x) & 0x0000ff00) >> 8)) #define minor_freebsd(x) ((int32_t)(((x) & 0xffff00ff) >> 0)) #define makedev_freebsd(x,y) ((portdev_t)((((x) << 8) & 0x0000ff00) | \ (((y) << 0) & 0xffff00ff))) static portdev_t pack_freebsd(int n, u_long numbers[], const char **error) { portdev_t dev = 0; if (n == 2) { dev = makedev_freebsd(numbers[0], numbers[1]); if ((u_long)major_freebsd(dev) != numbers[0]) *error = iMajorError; if ((u_long)minor_freebsd(dev) != numbers[1]) *error = iMinorError; } else *error = tooManyFields; return (dev); } #define major_8_8(x) ((int32_t)(((x) & 0x0000ff00) >> 8)) #define minor_8_8(x) ((int32_t)(((x) & 0x000000ff) >> 0)) #define makedev_8_8(x,y) ((portdev_t)((((x) << 8) & 0x0000ff00) | \ (((y) << 0) & 0x000000ff))) static portdev_t pack_8_8(int n, u_long numbers[], const char **error) { portdev_t dev = 0; if (n == 2) { dev = makedev_8_8(numbers[0], numbers[1]); if ((u_long)major_8_8(dev) != numbers[0]) *error = iMajorError; if ((u_long)minor_8_8(dev) != numbers[1]) *error = iMinorError; } else *error = tooManyFields; return (dev); } #define major_12_20(x) ((int32_t)(((x) & 0xfff00000) >> 20)) #define minor_12_20(x) ((int32_t)(((x) & 0x000fffff) >> 0)) #define makedev_12_20(x,y) ((portdev_t)((((x) << 20) & 0xfff00000) | \ (((y) << 0) & 0x000fffff))) static portdev_t pack_12_20(int n, u_long numbers[], const char **error) { portdev_t dev = 0; if (n == 2) { dev = makedev_12_20(numbers[0], numbers[1]); if ((u_long)major_12_20(dev) != numbers[0]) *error = iMajorError; if ((u_long)minor_12_20(dev) != numbers[1]) *error = iMinorError; } else *error = tooManyFields; return (dev); } #define major_14_18(x) ((int32_t)(((x) & 0xfffc0000) >> 18)) #define minor_14_18(x) ((int32_t)(((x) & 0x0003ffff) >> 0)) #define makedev_14_18(x,y) ((portdev_t)((((x) << 18) & 0xfffc0000) | \ (((y) << 0) & 0x0003ffff))) static portdev_t pack_14_18(int n, u_long numbers[], const char **error) { portdev_t dev = 0; if (n == 2) { dev = makedev_14_18(numbers[0], numbers[1]); if ((u_long)major_14_18(dev) != numbers[0]) *error = iMajorError; if ((u_long)minor_14_18(dev) != numbers[1]) *error = iMinorError; } else *error = tooManyFields; return (dev); } #define major_8_24(x) ((int32_t)(((x) & 0xff000000) >> 24)) #define minor_8_24(x) ((int32_t)(((x) & 0x00ffffff) >> 0)) #define makedev_8_24(x,y) ((portdev_t)((((x) << 24) & 0xff000000) | \ (((y) << 0) & 0x00ffffff))) static portdev_t pack_8_24(int n, u_long numbers[], const char **error) { portdev_t dev = 0; if (n == 2) { dev = makedev_8_24(numbers[0], numbers[1]); if ((u_long)major_8_24(dev) != numbers[0]) *error = iMajorError; if ((u_long)minor_8_24(dev) != numbers[1]) *error = iMinorError; } else *error = tooManyFields; return (dev); } #define major_12_12_8(x) ((int32_t)(((x) & 0xfff00000) >> 20)) #define unit_12_12_8(x) ((int32_t)(((x) & 0x000fff00) >> 8)) #define subunit_12_12_8(x) ((int32_t)(((x) & 0x000000ff) >> 0)) #define makedev_12_12_8(x,y,z) ((portdev_t)((((x) << 20) & 0xfff00000) | \ (((y) << 8) & 0x000fff00) | \ (((z) << 0) & 0x000000ff))) static portdev_t pack_bsdos(int n, u_long numbers[], const char **error) { portdev_t dev = 0; if (n == 2) { dev = makedev_12_20(numbers[0], numbers[1]); if ((u_long)major_12_20(dev) != numbers[0]) *error = iMajorError; if ((u_long)minor_12_20(dev) != numbers[1]) *error = iMinorError; } else if (n == 3) { dev = makedev_12_12_8(numbers[0], numbers[1], numbers[2]); if ((u_long)major_12_12_8(dev) != numbers[0]) *error = iMajorError; if ((u_long)unit_12_12_8(dev) != numbers[1]) *error = "invalid unit number"; if ((u_long)subunit_12_12_8(dev) != numbers[2]) *error = "invalid subunit number"; } else *error = tooManyFields; return (dev); } /* list of formats and pack functions */ /* this list must be sorted lexically */ struct format { const char *name; pack_t *pack; } formats[] = { {"386bsd", pack_8_8}, {"4bsd", pack_8_8}, {"bsdos", pack_bsdos}, {"freebsd", pack_freebsd}, {"hpux", pack_8_24}, {"isc", pack_8_8}, {"linux", pack_8_8}, {"native", pack_native}, {"netbsd", pack_netbsd}, {"osf1", pack_12_20}, {"sco", pack_8_8}, {"solaris", pack_14_18}, {"sunos", pack_8_8}, {"svr3", pack_8_8}, {"svr4", pack_14_18}, {"ultrix", pack_8_8}, }; static int compare_format(const void *key, const void *element) { const char *name; const struct format *format; name = key; format = element; return (strcmp(name, format->name)); } pack_t * pack_find(const char *name) { struct format *format; format = bsearch(name, formats, sizeof(formats)/sizeof(formats[0]), sizeof(formats[0]), compare_format); if (format == 0) return (NULL); return (format->pack); } makefs/src/usr.sbin/makefs/nbsrc/sbin/mknod/pack_dev.h010064400000000000000000000043011110267364000201500ustar00/** $MirOS: src/usr.sbin/makefs/nbsrc/sbin/mknod/pack_dev.h,v 1.4 2008/10/31 21:24:25 tg Exp $ */ /* $NetBSD: pack_dev.h,v 1.7 2008/04/28 20:23:09 martin Exp $ */ /*- * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Charles M. Hannum. * * 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. */ #ifndef _PACK_DEV_H #define _PACK_DEV_H #ifdef __CYGWIN__ typedef __dev32_t portdev_t; #else typedef dev_t portdev_t; #endif typedef portdev_t pack_t(int, u_long [], const char **); pack_t *pack_find(const char *); pack_t pack_native; #define major_netbsd(x) ((uint32_t)((((x) & 0x000fff00) >> 8))) #define minor_netbsd(x) ((uint32_t)((((x) & 0xfff00000) >> 12) | \ (((x) & 0x000000ff) >> 0))) #define makedev_netbsd(x,y) ((dev_t)((((x) << 8) & 0x000fff00) | \ (((y) << 12) & 0xfff00000) | \ (((y) << 0) & 0x000000ff))) #endif /* _PACK_DEV_H */ makefs/src/usr.sbin/makefs/nbsrc/sys/ufs/ffs/ffs_bswap.c010064400000000000000000000210111134453626700204770ustar00/* $NetBSD: ffs_bswap.c,v 1.34 2009/10/19 18:41:17 bouyer Exp $ */ /* * Copyright (c) 1998 Manuel Bouyer. * * 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. * */ #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #include __KERNEL_RCSID(0, "$NetBSD: ffs_bswap.c,v 1.34 2009/10/19 18:41:17 bouyer Exp $"); #include #if defined(_KERNEL) #include #endif #include #include #include #include #if !defined(_KERNEL) #include #include #include #include #define panic(x) printf("%s\n", (x)), abort() #endif void ffs_sb_swap(struct fs *o, struct fs *n) { size_t i; u_int32_t *o32, *n32; /* * In order to avoid a lot of lines, as the first N fields (52) * of the superblock up to fs_fmod are u_int32_t, we just loop * here to convert them. */ o32 = (u_int32_t *)o; n32 = (u_int32_t *)n; for (i = 0; i < offsetof(struct fs, fs_fmod) / sizeof(u_int32_t); i++) n32[i] = bswap32(o32[i]); n->fs_swuid = bswap64(o->fs_swuid); n->fs_cgrotor = bswap32(o->fs_cgrotor); /* Unused */ n->fs_old_cpc = bswap32(o->fs_old_cpc); /* These fields overlap with a possible location for the * historic FS_DYNAMICPOSTBLFMT postbl table, and with the * first half of the historic FS_42POSTBLFMT postbl table. */ n->fs_maxbsize = bswap32(o->fs_maxbsize); n->fs_sblockloc = bswap64(o->fs_sblockloc); ffs_csumtotal_swap(&o->fs_cstotal, &n->fs_cstotal); n->fs_time = bswap64(o->fs_time); n->fs_size = bswap64(o->fs_size); n->fs_dsize = bswap64(o->fs_dsize); n->fs_csaddr = bswap64(o->fs_csaddr); n->fs_pendingblocks = bswap64(o->fs_pendingblocks); n->fs_pendinginodes = bswap32(o->fs_pendinginodes); /* These fields overlap with the second half of the * historic FS_42POSTBLFMT postbl table */ for (i = 0; i < FSMAXSNAP; i++) n->fs_snapinum[i] = bswap32(o->fs_snapinum[i]); n->fs_avgfilesize = bswap32(o->fs_avgfilesize); n->fs_avgfpdir = bswap32(o->fs_avgfpdir); /* fs_sparecon[28] - ignore for now */ n->fs_flags = bswap32(o->fs_flags); n->fs_contigsumsize = bswap32(o->fs_contigsumsize); n->fs_maxsymlinklen = bswap32(o->fs_maxsymlinklen); n->fs_old_inodefmt = bswap32(o->fs_old_inodefmt); n->fs_maxfilesize = bswap64(o->fs_maxfilesize); n->fs_qbmask = bswap64(o->fs_qbmask); n->fs_qfmask = bswap64(o->fs_qfmask); n->fs_state = bswap32(o->fs_state); n->fs_old_postblformat = bswap32(o->fs_old_postblformat); n->fs_old_nrpos = bswap32(o->fs_old_nrpos); n->fs_old_postbloff = bswap32(o->fs_old_postbloff); n->fs_old_rotbloff = bswap32(o->fs_old_rotbloff); n->fs_magic = bswap32(o->fs_magic); } void ffs_dinode1_swap(struct ufs1_dinode *o, struct ufs1_dinode *n) { n->di_mode = bswap16(o->di_mode); n->di_nlink = bswap16(o->di_nlink); n->di_u.oldids[0] = bswap16(o->di_u.oldids[0]); n->di_u.oldids[1] = bswap16(o->di_u.oldids[1]); n->di_size = bswap64(o->di_size); n->di_atime = bswap32(o->di_atime); n->di_atimensec = bswap32(o->di_atimensec); n->di_mtime = bswap32(o->di_mtime); n->di_mtimensec = bswap32(o->di_mtimensec); n->di_ctime = bswap32(o->di_ctime); n->di_ctimensec = bswap32(o->di_ctimensec); memcpy(n->di_db, o->di_db, (NDADDR + NIADDR) * sizeof(u_int32_t)); n->di_flags = bswap32(o->di_flags); n->di_blocks = bswap32(o->di_blocks); n->di_gen = bswap32(o->di_gen); n->di_uid = bswap32(o->di_uid); n->di_gid = bswap32(o->di_gid); } void ffs_dinode2_swap(struct ufs2_dinode *o, struct ufs2_dinode *n) { n->di_mode = bswap16(o->di_mode); n->di_nlink = bswap16(o->di_nlink); n->di_uid = bswap32(o->di_uid); n->di_gid = bswap32(o->di_gid); n->di_blksize = bswap32(o->di_blksize); n->di_size = bswap64(o->di_size); n->di_blocks = bswap64(o->di_blocks); n->di_atime = bswap64(o->di_atime); n->di_atimensec = bswap32(o->di_atimensec); n->di_mtime = bswap64(o->di_mtime); n->di_mtimensec = bswap32(o->di_mtimensec); n->di_ctime = bswap64(o->di_ctime); n->di_ctimensec = bswap32(o->di_ctimensec); n->di_birthtime = bswap64(o->di_birthtime); n->di_birthnsec = bswap32(o->di_birthnsec); n->di_gen = bswap32(o->di_gen); n->di_kernflags = bswap32(o->di_kernflags); n->di_flags = bswap32(o->di_flags); n->di_extsize = bswap32(o->di_extsize); memcpy(n->di_extb, o->di_extb, (NXADDR + NDADDR + NIADDR) * 8); } void ffs_csum_swap(struct csum *o, struct csum *n, int size) { size_t i; u_int32_t *oint, *nint; oint = (u_int32_t*)o; nint = (u_int32_t*)n; for (i = 0; i < size / sizeof(u_int32_t); i++) nint[i] = bswap32(oint[i]); } void ffs_csumtotal_swap(struct csum_total *o, struct csum_total *n) { n->cs_ndir = bswap64(o->cs_ndir); n->cs_nbfree = bswap64(o->cs_nbfree); n->cs_nifree = bswap64(o->cs_nifree); n->cs_nffree = bswap64(o->cs_nffree); } /* * Note that ffs_cg_swap may be called with o == n. */ void ffs_cg_swap(struct cg *o, struct cg *n, struct fs *fs) { int i; u_int32_t *n32, *o32; u_int16_t *n16, *o16; int32_t btotoff, boff, clustersumoff; n->cg_firstfield = bswap32(o->cg_firstfield); n->cg_magic = bswap32(o->cg_magic); n->cg_old_time = bswap32(o->cg_old_time); n->cg_cgx = bswap32(o->cg_cgx); n->cg_old_ncyl = bswap16(o->cg_old_ncyl); n->cg_old_niblk = bswap16(o->cg_old_niblk); n->cg_ndblk = bswap32(o->cg_ndblk); n->cg_cs.cs_ndir = bswap32(o->cg_cs.cs_ndir); n->cg_cs.cs_nbfree = bswap32(o->cg_cs.cs_nbfree); n->cg_cs.cs_nifree = bswap32(o->cg_cs.cs_nifree); n->cg_cs.cs_nffree = bswap32(o->cg_cs.cs_nffree); n->cg_rotor = bswap32(o->cg_rotor); n->cg_frotor = bswap32(o->cg_frotor); n->cg_irotor = bswap32(o->cg_irotor); for (i = 0; i < MAXFRAG; i++) n->cg_frsum[i] = bswap32(o->cg_frsum[i]); if ((fs->fs_magic != FS_UFS2_MAGIC) && (fs->fs_old_postblformat == FS_42POSTBLFMT)) { /* old format */ struct ocg *on, *oo; int j; on = (struct ocg *)n; oo = (struct ocg *)o; for (i = 0; i < 32; i++) { on->cg_btot[i] = bswap32(oo->cg_btot[i]); for (j = 0; j < 8; j++) on->cg_b[i][j] = bswap16(oo->cg_b[i][j]); } memmove(on->cg_iused, oo->cg_iused, 256); on->cg_magic = bswap32(oo->cg_magic); } else { /* new format */ n->cg_old_btotoff = bswap32(o->cg_old_btotoff); n->cg_old_boff = bswap32(o->cg_old_boff); n->cg_iusedoff = bswap32(o->cg_iusedoff); n->cg_freeoff = bswap32(o->cg_freeoff); n->cg_nextfreeoff = bswap32(o->cg_nextfreeoff); n->cg_clustersumoff = bswap32(o->cg_clustersumoff); n->cg_clusteroff = bswap32(o->cg_clusteroff); n->cg_nclusterblks = bswap32(o->cg_nclusterblks); n->cg_niblk = bswap32(o->cg_niblk); n->cg_initediblk = bswap32(o->cg_initediblk); n->cg_time = bswap64(o->cg_time); if (n->cg_magic == CG_MAGIC) { btotoff = n->cg_old_btotoff; boff = n->cg_old_boff; clustersumoff = n->cg_clustersumoff; } else { btotoff = bswap32(n->cg_old_btotoff); boff = bswap32(n->cg_old_boff); clustersumoff = bswap32(n->cg_clustersumoff); } n32 = (u_int32_t *)((u_int8_t *)n + clustersumoff); o32 = (u_int32_t *)((u_int8_t *)o + clustersumoff); for (i = 1; i < fs->fs_contigsumsize + 1; i++) n32[i] = bswap32(o32[i]); if (fs->fs_magic == FS_UFS2_MAGIC) return; n32 = (u_int32_t *)((u_int8_t *)n + btotoff); o32 = (u_int32_t *)((u_int8_t *)o + btotoff); n16 = (u_int16_t *)((u_int8_t *)n + boff); o16 = (u_int16_t *)((u_int8_t *)o + boff); for (i = 0; i < fs->fs_old_cpg; i++) n32[i] = bswap32(o32[i]); for (i = 0; i < fs->fs_old_cpg * fs->fs_old_nrpos; i++) n16[i] = bswap16(o16[i]); } } makefs/src/usr.sbin/makefs/nbsrc/sys/ufs/ffs/ffs_extern.h010064400000000000000000000165141134453600100206730ustar00/* $NetBSD: ffs_extern.h,v 1.75 2009/02/22 20:28:06 ad Exp $ */ /*- * Copyright (c) 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. * * @(#)ffs_extern.h 8.6 (Berkeley) 3/30/95 */ #ifndef _UFS_FFS_FFS_EXTERN_H_ #define _UFS_FFS_FFS_EXTERN_H_ /* * Sysctl values for the fast filesystem. */ #define FFS_CLUSTERREAD 1 /* cluster reading enabled */ #define FFS_CLUSTERWRITE 2 /* cluster writing enabled */ #define FFS_REALLOCBLKS 3 /* block reallocation enabled */ #define FFS_ASYNCFREE 4 /* asynchronous block freeing enabled */ #define FFS_LOG_CHANGEOPT 5 /* log optimalization strategy change */ #define FFS_MAXID 6 /* number of valid ffs ids */ struct buf; struct fid; struct fs; struct inode; struct ufs1_dinode; struct ufs2_dinode; struct mount; struct nameidata; struct lwp; struct statvfs; struct timeval; struct timespec; struct ufsmount; struct uio; struct vnode; struct mbuf; struct cg; #if defined(_KERNEL) #include #define FFS_NOBLK ((daddr_t)-1) #define FFS_ITIMES(ip, acc, mod, cre) \ while ((ip)->i_flag & (IN_ACCESS | IN_CHANGE | IN_UPDATE | IN_MODIFY)) \ ffs_itimes(ip, acc, mod, cre) extern pool_cache_t ffs_inode_cache; /* memory pool for inodes */ extern pool_cache_t ffs_dinode1_cache; /* memory pool for UFS1 dinodes */ extern pool_cache_t ffs_dinode2_cache; /* memory pool for UFS2 dinodes */ #endif /* defined(_KERNEL) */ __BEGIN_DECLS #if defined(_KERNEL) #include #include #include /* ffs_alloc.c */ int ffs_alloc(struct inode *, daddr_t, daddr_t , int, int, kauth_cred_t, daddr_t *); int ffs_realloccg(struct inode *, daddr_t, daddr_t, int, int , kauth_cred_t, struct buf **, daddr_t *); int ffs_valloc(struct vnode *, int, kauth_cred_t, struct vnode **); daddr_t ffs_blkpref_ufs1(struct inode *, daddr_t, int, int, int32_t *); daddr_t ffs_blkpref_ufs2(struct inode *, daddr_t, int, int, int64_t *); int ffs_blkalloc(struct inode *, daddr_t, long); int ffs_blkalloc_ump(struct ufsmount *, daddr_t, long); void ffs_blkfree(struct fs *, struct vnode *, daddr_t, long, ino_t); void ffs_blkfree_snap(struct fs *, struct vnode *, daddr_t, long, ino_t); int ffs_vfree(struct vnode *, ino_t, int); void ffs_clusteracct(struct fs *, struct cg *, int32_t, int); int ffs_checkfreefile(struct fs *, struct vnode *, ino_t); int ffs_freefile(struct mount *, ino_t, int); int ffs_freefile_snap(struct fs *, struct vnode *, ino_t, int); /* ffs_balloc.c */ int ffs_balloc(struct vnode *, off_t, int, kauth_cred_t, int, struct buf **); /* ffs_inode.c */ int ffs_update(struct vnode *, const struct timespec *, const struct timespec *, int); int ffs_truncate(struct vnode *, off_t, int, kauth_cred_t); /* ffs_vfsops.c */ VFS_PROTOS(ffs); int ffs_reload(struct mount *, kauth_cred_t, struct lwp *); int ffs_mountfs(struct vnode *, struct mount *, struct lwp *); int ffs_flushfiles(struct mount *, int, struct lwp *); int ffs_sbupdate(struct ufsmount *, int); int ffs_cgupdate(struct ufsmount *, int); /* ffs_vnops.c */ int ffs_read(void *); int ffs_write(void *); int ffs_fsync(void *); int ffs_reclaim(void *); int ffs_getpages(void *); void ffs_gop_size(struct vnode *, off_t, off_t *, int); int ffs_openextattr(void *); int ffs_closeextattr(void *); int ffs_getextattr(void *); int ffs_setextattr(void *); int ffs_listextattr(void *); int ffs_deleteextattr(void *); int ffs_lock(void *); int ffs_unlock(void *); int ffs_islocked(void *); int ffs_full_fsync(struct vnode *, int); /* * Snapshot function prototypes. */ int ffs_snapshot_init(struct ufsmount *); void ffs_snapshot_fini(struct ufsmount *); int ffs_snapblkfree(struct fs *, struct vnode *, daddr_t, long, ino_t); void ffs_snapremove(struct vnode *); int ffs_snapshot(struct mount *, struct vnode *, struct timespec *); void ffs_snapshot_mount(struct mount *); void ffs_snapshot_unmount(struct mount *); void ffs_snapgone(struct inode *); int ffs_snapshot_read(struct vnode *, struct uio *, int); /* Write Ahead Physical Block Logging */ void ffs_wapbl_verify_inodes(struct mount *, const char *); void ffs_wapbl_replay_finish(struct mount *); int ffs_wapbl_start(struct mount *); int ffs_wapbl_stop(struct mount *, int); int ffs_wapbl_replay_start(struct mount *, struct fs *, struct vnode *); void ffs_wapbl_blkalloc(struct fs *, struct vnode *, daddr_t, int); void ffs_wapbl_sync_metadata(struct mount *, daddr_t *, int *, int); void ffs_wapbl_abort_sync_metadata(struct mount *, daddr_t *, int *, int); extern int (**ffs_vnodeop_p)(void *); extern int (**ffs_specop_p)(void *); extern int (**ffs_fifoop_p)(void *); #endif /* defined(_KERNEL) */ /* ffs_appleufs.c */ struct appleufslabel; u_int16_t ffs_appleufs_cksum(const struct appleufslabel *); int ffs_appleufs_validate(const char*, const struct appleufslabel *, struct appleufslabel *); void ffs_appleufs_set(struct appleufslabel *, const char *, time_t, uint64_t); /* ffs_bswap.c */ void ffs_sb_swap(struct fs*, struct fs *); void ffs_dinode1_swap(struct ufs1_dinode *, struct ufs1_dinode *); void ffs_dinode2_swap(struct ufs2_dinode *, struct ufs2_dinode *); struct csum; void ffs_csum_swap(struct csum *, struct csum *, int); struct csum_total; void ffs_csumtotal_swap(struct csum_total *, struct csum_total *); void ffs_cg_swap(struct cg *, struct cg *, struct fs *); /* ffs_subr.c */ #if defined(_KERNEL) void ffs_load_inode(struct buf *, struct inode *, struct fs *, ino_t); int ffs_getblk(struct vnode *, daddr_t, daddr_t, int, bool, buf_t **); #endif /* defined(_KERNEL) */ void ffs_fragacct(struct fs *, int, int32_t[], int, int); int ffs_isblock(struct fs *, u_char *, int32_t); int ffs_isfreeblock(struct fs *, u_char *, int32_t); void ffs_clrblock(struct fs *, u_char *, int32_t); void ffs_setblock(struct fs *, u_char *, int32_t); void ffs_itimes(struct inode *, const struct timespec *, const struct timespec *, const struct timespec *); __END_DECLS #endif /* !_UFS_FFS_FFS_EXTERN_H_ */ makefs/src/usr.sbin/makefs/nbsrc/sys/ufs/ffs/ffs_subr.c010064400000000000000000000160171223453423000203330ustar00/* $MirOS: src/usr.sbin/makefs/nbsrc/sys/ufs/ffs/ffs_subr.c,v 1.3 2013/10/31 20:07:27 tg Exp $ */ /* $NetBSD: ffs_subr.c,v 1.45 2008/06/03 09:47:49 hannken Exp $ */ /* * Copyright © 2013 * Thorsten “mirabilos” Glaser * Copyright (c) 1982, 1986, 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. * * @(#)ffs_subr.c 8.5 (Berkeley) 3/21/95 */ #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #include __KERNEL_RCSID(0, "$NetBSD: ffs_subr.c,v 1.45 2008/06/03 09:47:49 hannken Exp $"); #include /* in ffs_tables.c */ extern const int inside[], around[]; extern const u_char * const fragtbl[]; #include #include #include #ifndef _KERNEL #include void panic(const char *, ...) __attribute__((__noreturn__, __format__(__printf__, 1, 2))); #else /* _KERNEL */ #include #include #include #include #include #include #include #include #include #include /* * Load up the contents of an inode and copy the appropriate pieces * to the incore copy. */ void ffs_load_inode(struct buf *bp, struct inode *ip, struct fs *fs, ino_t ino) { struct ufs1_dinode *dp1; struct ufs2_dinode *dp2; if (ip->i_ump->um_fstype == UFS1) { dp1 = (struct ufs1_dinode *)bp->b_data + ino_to_fsbo(fs, ino); #ifdef FFS_EI if (UFS_FSNEEDSWAP(fs)) ffs_dinode1_swap(dp1, ip->i_din.ffs1_din); else #endif *ip->i_din.ffs1_din = *dp1; ip->i_mode = ip->i_ffs1_mode; ip->i_nlink = ip->i_ffs1_nlink; ip->i_size = ip->i_ffs1_size; ip->i_flags = ip->i_ffs1_flags; ip->i_gen = ip->i_ffs1_gen; ip->i_uid = ip->i_ffs1_uid; ip->i_gid = ip->i_ffs1_gid; } else { dp2 = (struct ufs2_dinode *)bp->b_data + ino_to_fsbo(fs, ino); #ifdef FFS_EI if (UFS_FSNEEDSWAP(fs)) ffs_dinode2_swap(dp2, ip->i_din.ffs2_din); else #endif *ip->i_din.ffs2_din = *dp2; ip->i_mode = ip->i_ffs2_mode; ip->i_nlink = ip->i_ffs2_nlink; ip->i_size = ip->i_ffs2_size; ip->i_flags = ip->i_ffs2_flags; ip->i_gen = ip->i_ffs2_gen; ip->i_uid = ip->i_ffs2_uid; ip->i_gid = ip->i_ffs2_gid; } } int ffs_getblk(struct vnode *vp, daddr_t lblkno, daddr_t blkno, int size, bool clearbuf, buf_t **bpp) { int error = 0; KASSERT(blkno >= 0 || blkno == FFS_NOBLK); if ((*bpp = getblk(vp, lblkno, size, 0, 0)) == NULL) return ENOMEM; if (blkno != FFS_NOBLK) (*bpp)->b_blkno = blkno; if (clearbuf) clrbuf(*bpp); if ((*bpp)->b_blkno >= 0 && (error = fscow_run(*bpp, false)) != 0) brelse(*bpp, BC_INVAL); return error; } #endif /* _KERNEL */ /* * Update the frsum fields to reflect addition or deletion * of some frags. */ void ffs_fragacct(struct fs *fs, int fragmap, int32_t fraglist[], int cnt, int needswap) { int inblk; int field, subfield; int siz, pos; inblk = (int)(fragtbl[fs->fs_frag][fragmap]) << 1; fragmap <<= 1; for (siz = 1; siz < fs->fs_frag; siz++) { if ((inblk & (1 << (siz + (fs->fs_frag & (NBBY - 1))))) == 0) continue; field = around[siz]; subfield = inside[siz]; for (pos = siz; pos <= fs->fs_frag; pos++) { if ((fragmap & field) == subfield) { fraglist[siz] = ufs_rw32( ufs_rw32(fraglist[siz], needswap) + cnt, needswap); pos += siz; field <<= siz; subfield <<= siz; } field <<= 1; subfield <<= 1; } } } /* * block operations * * check if a block is available * returns true if all the correponding bits in the free map are 1 * returns false if any corresponding bit in the free map is 0 */ int ffs_isblock(struct fs *fs, u_char *cp, int32_t h) { u_char mask; switch ((int)fs->fs_fragshift) { case 3: return (cp[h] == 0xff); case 2: mask = 0x0f << ((h & 0x1) << 2); return ((cp[h >> 1] & mask) == mask); case 1: mask = 0x03 << ((h & 0x3) << 1); return ((cp[h >> 2] & mask) == mask); case 0: mask = 0x01 << (h & 0x7); return ((cp[h >> 3] & mask) == mask); default: panic("ffs_isblock: unknown fs_fragshift %d", (int)fs->fs_fragshift); } } /* * check if a block is completely allocated * returns true if all the corresponding bits in the free map are 0 * returns false if any corresponding bit in the free map is 1 */ int ffs_isfreeblock(struct fs *fs, u_char *cp, int32_t h) { switch ((int)fs->fs_fragshift) { case 3: return (cp[h] == 0); case 2: return ((cp[h >> 1] & (0x0f << ((h & 0x1) << 2))) == 0); case 1: return ((cp[h >> 2] & (0x03 << ((h & 0x3) << 1))) == 0); case 0: return ((cp[h >> 3] & (0x01 << (h & 0x7))) == 0); default: panic("ffs_isfreeblock: unknown fs_fragshift %d", (int)fs->fs_fragshift); } } /* * take a block out of the map */ void ffs_clrblock(struct fs *fs, u_char *cp, int32_t h) { switch ((int)fs->fs_fragshift) { case 3: cp[h] = 0; return; case 2: cp[h >> 1] &= ~(0x0f << ((h & 0x1) << 2)); return; case 1: cp[h >> 2] &= ~(0x03 << ((h & 0x3) << 1)); return; case 0: cp[h >> 3] &= ~(0x01 << (h & 0x7)); return; default: panic("ffs_clrblock: unknown fs_fragshift %d", (int)fs->fs_fragshift); } } /* * put a block into the map */ void ffs_setblock(struct fs *fs, u_char *cp, int32_t h) { switch ((int)fs->fs_fragshift) { case 3: cp[h] = 0xff; return; case 2: cp[h >> 1] |= (0x0f << ((h & 0x1) << 2)); return; case 1: cp[h >> 2] |= (0x03 << ((h & 0x3) << 1)); return; case 0: cp[h >> 3] |= (0x01 << (h & 0x7)); return; default: panic("ffs_setblock: unknown fs_fragshift %d", (int)fs->fs_fragshift); } } makefs/src/usr.sbin/makefs/nbsrc/sys/ufs/ffs/fs.h010064400000000000000000000751661314214553400171550ustar00/** $MirOS: src/usr.sbin/makefs/nbsrc/sys/ufs/ffs/fs.h,v 1.7 2017/08/07 20:19:13 tg Exp $ */ /* $NetBSD: fs.h,v 1.55 2010/01/31 10:54:10 mlelstv Exp $ */ /* * Copyright (c) 1982, 1986, 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. * * @(#)fs.h 8.13 (Berkeley) 3/21/95 */ /* * NOTE: COORDINATE ON-DISK FORMAT CHANGES WITH THE FREEBSD PROJECT. */ #ifndef _UFS_FFS_FS_H_ #define _UFS_FFS_FS_H_ /* * Each disk drive contains some number of filesystems. * A filesystem consists of a number of cylinder groups. * Each cylinder group has inodes and data. * * A filesystem is described by its super-block, which in turn * describes the cylinder groups. The super-block is critical * data and is replicated in each cylinder group to protect against * catastrophic loss. This is done at `newfs' time and the critical * super-block data does not change, so the copies need not be * referenced further unless disaster strikes. * * For filesystem fs, the offsets of the various blocks of interest * are given in the super block as: * [fs->fs_sblkno] Super-block * [fs->fs_cblkno] Cylinder group block * [fs->fs_iblkno] Inode blocks * [fs->fs_dblkno] Data blocks * The beginning of cylinder group cg in fs, is given by * the ``cgbase(fs, cg)'' macro. * * Depending on the architecture and the media, the superblock may * reside in any one of four places. For tiny media where every block * counts, it is placed at the very front of the partition. Historically, * UFS1 placed it 8K from the front to leave room for the disk label and * a small bootstrap. For UFS2 it got moved to 64K from the front to leave * room for the disk label and a bigger bootstrap, and for really piggy * systems we check at 256K from the front if the first three fail. In * all cases the size of the superblock will be SBLOCKSIZE. All values are * given in byte-offset form, so they do not imply a sector size. The * SBLOCKSEARCH specifies the order in which the locations should be searched. * * Unfortunately the UFS2/FFSv2 change was done without adequate consideration * of backward compatibility. In particular 'newfs' for a FFSv2 partition * must overwrite any old FFSv1 superblock at 8k, and preferrably as many * of the alternates as it can find - otherwise attempting to mount on a * system that only supports FFSv1 is likely to succeed!. * For a small FFSv1 filesystem, an old FFSv2 superblock can be left on * the disk, and a system that tries to find an FFSv2 filesystem in preference * to and FFSv1 one (as NetBSD does) can mount the old FFSv2 filesystem. * As a added bonus, the 'first alternate' superblock of a FFSv1 filesystem * with 64k blocks is at 64k - just where the code looks first when playing * 'hunt the superblock'. * * The ffsv2 superblock layout (which might contain an ffsv1 filesystem) * can be detected by checking for sb->fs_old_flags & FS_FLAGS_UPDATED. * This is the default superblock type for NetBSD since ffsv2 support was added. */ #define BBSIZE 8192 #define BBOFF ((off_t)(0)) #define BBLOCK ((daddr_t)(0)) #define SBLOCK_FLOPPY 0 #define SBLOCK_UFS1 8192 #define SBLOCK_UFS2 65536 #define SBLOCK_PIGGY 262144 #define SBLOCKSIZE 8192 /* * NB: Do not, under any circumstances, look for an ffsv1 filesystem at * SBLOCK_UFS2. Doing so will find the wrong superblock for filesystems * with a 64k block size. */ #define SBLOCKSEARCH \ { SBLOCK_UFS2, SBLOCK_UFS1, SBLOCK_FLOPPY, SBLOCK_PIGGY, -1 } /* * Max number of fragments per block. This value is NOT tweakable. */ #define MAXFRAG 8 /* * Addresses stored in inodes are capable of addressing fragments * of `blocks'. Filesystem blocks of at most size MAXBSIZE can * be optionally broken into 2, 4, or 8 pieces, each of which is * addressable; these pieces may be DEV_BSIZE, or some multiple of * a DEV_BSIZE unit. * * Large files consist of exclusively large data blocks. To avoid * undue wasted disk space, the last data block of a small file may be * allocated as only as many fragments of a large block as are * necessary. The filesystem format retains only a single pointer * to such a fragment, which is a piece of a single large block that * has been divided. The size of such a fragment is determinable from * information in the inode, using the ``blksize(fs, ip, lbn)'' macro. * * The filesystem records space availability at the fragment level; * to determine block availability, aligned fragments are examined. */ /* * MINBSIZE is the smallest allowable block size. * In order to insure that it is possible to create files of size * 2^32 with only two levels of indirection, MINBSIZE is set to 4096. * MINBSIZE must be big enough to hold a cylinder group block, * thus changes to (struct cg) must keep its size within MINBSIZE. * Note that super blocks are always of size SBSIZE, * and that both SBSIZE and MAXBSIZE must be >= MINBSIZE. */ #define MINBSIZE 4096 /* * The path name on which the filesystem is mounted is maintained * in fs_fsmnt. MAXMNTLEN defines the amount of space allocated in * the super block for this name. */ #define MAXMNTLEN 468 /* * The volume name for this filesystem is maintained in fs_volname. * MAXVOLLEN defines the length of the buffer allocated. * This space used to be part of of fs_fsmnt. */ #define MAXVOLLEN 32 /* * There is a 128-byte region in the superblock reserved for in-core * pointers to summary information. Originally this included an array * of pointers to blocks of struct csum; now there are just four * pointers and the remaining space is padded with fs_ocsp[]. * NOCSPTRS determines the size of this padding. One pointer (fs_csp) * is taken away to point to a contiguous array of struct csum for * all cylinder groups; a second (fs_maxcluster) points to an array * of cluster sizes that is computed as cylinder groups are inspected; * the third (fs_contigdirs) points to an array that tracks the * creation of new directories; and the fourth (fs_active) is used * by snapshots. */ #define NOCSPTRS ((128 / sizeof(void *)) - 4) /* * A summary of contiguous blocks of various sizes is maintained * in each cylinder group. Normally this is set by the initial * value of fs_maxcontig. To conserve space, a maximum summary size * is set by FS_MAXCONTIG. */ #define FS_MAXCONTIG 16 /* * The maximum number of snapshot nodes that can be associated * with each filesystem. This limit affects only the number of * snapshot files that can be recorded within the superblock so * that they can be found when the filesystem is mounted. However, * maintaining too many will slow the filesystem performance, so * having this limit is a good idea. */ #define FSMAXSNAP 20 /* * Used to identify special blocks in snapshots: * * BLK_NOCOPY - A block that was unallocated at the time the snapshot * was taken, hence does not need to be copied when written. * BLK_SNAP - A block held by another snapshot that is not needed by this * snapshot. When the other snapshot is freed, the BLK_SNAP entries * are converted to BLK_NOCOPY. These are needed to allow fsck to * identify blocks that are in use by other snapshots (which are * expunged from this snapshot). */ #define BLK_NOCOPY ((daddr_t)(1)) #define BLK_SNAP ((daddr_t)(2)) /* * MINFREE gives the minimum acceptable percentage of filesystem * blocks which may be free. If the freelist drops below this level * only the superuser may continue to allocate blocks. This may * be set to 0 if no reserve of free blocks is deemed necessary, * however throughput drops by fifty percent if the filesystem * is run at between 95% and 100% full; thus the minimum default * value of fs_minfree is 5%. However, to get good clustering * performance, 10% is a better choice. This value is used only * when creating a filesystem and can be overriden from the * command line. By default we choose to optimize for time. */ #define MINFREE 5 #define DEFAULTOPT FS_OPTTIME /* * Grigoriy Orlov has done some extensive work to fine * tune the layout preferences for directories within a filesystem. * His algorithm can be tuned by adjusting the following parameters * which tell the system the average file size and the average number * of files per directory. These defaults are well selected for typical * filesystems, but may need to be tuned for odd cases like filesystems * being used for squid caches or news spools. */ #define AVFILESIZ 16384 /* expected average file size */ #define AFPDIR 64 /* expected number of files per directory */ /* * Per cylinder group information; summarized in blocks allocated * from first cylinder group data blocks. These blocks have to be * read in from fs_csaddr (size fs_cssize) in addition to the * super block. */ struct csum { int32_t cs_ndir; /* number of directories */ int32_t cs_nbfree; /* number of free blocks */ int32_t cs_nifree; /* number of free inodes */ int32_t cs_nffree; /* number of free frags */ }; struct csum_total { int64_t cs_ndir; /* number of directories */ int64_t cs_nbfree; /* number of free blocks */ int64_t cs_nifree; /* number of free inodes */ int64_t cs_nffree; /* number of free frags */ int64_t cs_spare[4]; /* future expansion */ }; /* * Super block for an FFS filesystem in memory. */ struct fs { int32_t fs_historic_start[2]; /* used by MirBSD to store entropy */ #define fs_firstfield fs_historic_start[0] /* filesystem linked list, */ #define fs_unused_1 fs_historic_start[1] /* used for incore superblk */ int32_t fs_sblkno; /* addr of super-block in filesys */ int32_t fs_cblkno; /* offset of cyl-block in filesys */ int32_t fs_iblkno; /* offset of inode-blocks in filesys */ int32_t fs_dblkno; /* offset of first data after cg */ int32_t fs_old_cgoffset; /* cylinder group offset in cylinder */ int32_t fs_old_cgmask; /* used to calc mod fs_ntrak */ int32_t fs_old_time; /* last time written */ int32_t fs_old_size; /* number of blocks in fs */ int32_t fs_old_dsize; /* number of data blocks in fs */ int32_t fs_ncg; /* number of cylinder groups */ int32_t fs_bsize; /* size of basic blocks in fs */ int32_t fs_fsize; /* size of frag blocks in fs */ int32_t fs_frag; /* number of frags in a block in fs */ /* these are configuration parameters */ int32_t fs_minfree; /* minimum percentage of free blocks */ int32_t fs_old_rotdelay; /* num of ms for optimal next block */ int32_t fs_old_rps; /* disk revolutions per second */ /* these fields can be computed from the others */ int32_t fs_bmask; /* ``blkoff'' calc of blk offsets */ int32_t fs_fmask; /* ``fragoff'' calc of frag offsets */ int32_t fs_bshift; /* ``lblkno'' calc of logical blkno */ int32_t fs_fshift; /* ``numfrags'' calc number of frags */ /* these are configuration parameters */ int32_t fs_maxcontig; /* max number of contiguous blks */ int32_t fs_maxbpg; /* max number of blks per cyl group */ /* these fields can be computed from the others */ int32_t fs_fragshift; /* block to frag shift */ int32_t fs_fsbtodb; /* fsbtodb and dbtofsb shift constant */ int32_t fs_sbsize; /* actual size of super block */ int32_t fs_spare1[2]; /* old fs_csmask */ /* old fs_csshift */ int32_t fs_nindir; /* value of NINDIR */ int32_t fs_inopb; /* value of INOPB */ int32_t fs_old_nspf; /* value of NSPF */ /* yet another configuration parameter */ int32_t fs_optim; /* optimization preference, see below */ /* these fields are derived from the hardware */ int32_t fs_old_npsect; /* # sectors/track including spares */ int32_t fs_old_interleave; /* hardware sector interleave */ int32_t fs_old_trackskew; /* sector 0 skew, per track */ /* fs_id takes the space of the unused fs_headswitch and fs_trkseek fields */ int32_t fs_id[2]; /* unique filesystem id */ /* sizes determined by number of cylinder groups and their sizes */ int32_t fs_old_csaddr; /* blk addr of cyl grp summary area */ int32_t fs_cssize; /* size of cyl grp summary area */ int32_t fs_cgsize; /* cylinder group size */ /* these fields are derived from the hardware */ int32_t fs_spare2; /* old fs_ntrak */ int32_t fs_old_nsect; /* sectors per track */ int32_t fs_old_spc; /* sectors per cylinder */ int32_t fs_old_ncyl; /* cylinders in filesystem */ int32_t fs_old_cpg; /* cylinders per group */ int32_t fs_ipg; /* inodes per group */ int32_t fs_fpg; /* blocks per group * fs_frag */ /* this data must be re-computed after crashes */ struct csum fs_old_cstotal; /* cylinder summary information */ /* these fields are cleared at mount time */ int8_t fs_fmod; /* super block modified flag */ uint8_t fs_clean; /* filesystem is clean flag */ int8_t fs_ronly; /* mounted read-only flag */ uint8_t fs_old_flags; /* see FS_ flags below */ u_char fs_fsmnt[MAXMNTLEN]; /* name mounted on */ u_char fs_volname[MAXVOLLEN]; /* volume name */ uint64_t fs_swuid; /* system-wide uid */ int32_t fs_pad; /* these fields retain the current block allocation info */ int32_t fs_cgrotor; /* last cg searched (UNUSED) */ void *fs_ocsp[NOCSPTRS]; /* padding; was list of fs_cs buffers */ u_int8_t *fs_contigdirs; /* # of contiguously allocated dirs */ struct csum *fs_csp; /* cg summary info buffer for fs_cs */ int32_t *fs_maxcluster; /* max cluster in each cyl group */ u_char *fs_active; /* used by snapshots to track fs */ int32_t fs_old_cpc; /* cyl per cycle in postbl */ /* this area is otherwise allocated unless fs_old_flags & FS_FLAGS_UPDATED */ int32_t fs_maxbsize; /* maximum blocking factor permitted */ uint8_t fs_journal_version; /* journal format version */ uint8_t fs_journal_location; /* journal location type */ uint8_t fs_journal_reserved[2];/* reserved for future use */ uint32_t fs_journal_flags; /* journal flags */ uint64_t fs_journallocs[4]; /* location info for journal */ int64_t fs_sparecon64[12]; /* reserved for future use */ int64_t fs_sblockloc; /* byte offset of standard superblock */ struct csum_total fs_cstotal; /* cylinder summary information */ int64_t fs_time; /* last time written */ int64_t fs_size; /* number of blocks in fs */ int64_t fs_dsize; /* number of data blocks in fs */ int64_t fs_csaddr; /* blk addr of cyl grp summary area */ int64_t fs_pendingblocks; /* blocks in process of being freed */ int32_t fs_pendinginodes; /* inodes in process of being freed */ int32_t fs_snapinum[FSMAXSNAP];/* list of snapshot inode numbers */ /* back to stuff that has been around a while */ int32_t fs_avgfilesize; /* expected average file size */ int32_t fs_avgfpdir; /* expected # of files per directory */ int32_t fs_save_cgsize; /* save real cg size to use fs_bsize */ int32_t fs_sparecon32[26]; /* reserved for future constants */ uint32_t fs_flags; /* see FS_ flags below */ /* back to stuff that has been around a while (again) */ int32_t fs_contigsumsize; /* size of cluster summary array */ int32_t fs_maxsymlinklen; /* max length of an internal symlink */ int32_t fs_old_inodefmt; /* format of on-disk inodes */ u_int64_t fs_maxfilesize; /* maximum representable file size */ int64_t fs_qbmask; /* ~fs_bmask for use with 64-bit size */ int64_t fs_qfmask; /* ~fs_fmask for use with 64-bit size */ int32_t fs_state; /* validate fs_clean field (UNUSED) */ int32_t fs_old_postblformat; /* format of positional layout tables */ int32_t fs_old_nrpos; /* number of rotational positions */ int32_t fs_spare5[2]; /* old fs_postbloff */ /* old fs_rotbloff */ int32_t fs_magic; /* magic number */ }; #define fs_old_postbloff fs_spare5[0] #define fs_old_rotbloff fs_spare5[1] #define fs_old_postbl_start fs_maxbsize #define fs_old_headswitch fs_id[0] #define fs_old_trkseek fs_id[1] #define fs_old_csmask fs_spare1[0] #define fs_old_csshift fs_spare1[1] #define FS_42POSTBLFMT -1 /* 4.2BSD rotational table format */ #define FS_DYNAMICPOSTBLFMT 1 /* dynamic rotational table format */ #define old_fs_postbl(fs_, cylno, opostblsave) \ ((((fs_)->fs_old_postblformat == FS_42POSTBLFMT) || \ ((fs_)->fs_old_postbloff == offsetof(struct fs, fs_old_postbl_start))) \ ? ((int16_t *)(opostblsave) + (cylno) * (fs_)->fs_old_nrpos) \ : ((int16_t *)((uint8_t *)(fs_) + \ (fs_)->fs_old_postbloff) + (cylno) * (fs_)->fs_old_nrpos)) #define old_fs_rotbl(fs) \ (((fs)->fs_old_postblformat == FS_42POSTBLFMT) \ ? ((uint8_t *)(&(fs)->fs_magic+1)) \ : ((uint8_t *)((uint8_t *)(fs) + (fs)->fs_old_rotbloff))) /* * Filesystem identification */ #define FS_UFS1_MAGIC 0x011954 /* UFS1 fast filesystem magic number */ #define FS_UFS2_MAGIC 0x19540119 /* UFS2 fast filesystem magic number */ #define FS_UFS1_MAGIC_SWAPPED 0x54190100 #define FS_UFS2_MAGIC_SWAPPED 0x19015419 #define FS_OKAY 0x7c269d38 /* superblock checksum */ #define FS_42INODEFMT -1 /* 4.2BSD inode format */ #define FS_44INODEFMT 2 /* 4.4BSD inode format */ /* * Filesystem clean flags */ #define FS_ISCLEAN 0x01 #define FS_WASCLEAN 0x02 /* * Preference for optimization. */ #define FS_OPTTIME 0 /* minimize allocation time */ #define FS_OPTSPACE 1 /* minimize disk fragmentation */ /* * Filesystem flags */ #define FS_UNCLEAN 0x001 /* filesystem not clean at mount (unused) */ #define FS_DOSOFTDEP 0x002 /* filesystem using soft dependencies */ #define FS_NEEDSFSCK 0x004 /* needs sync fsck (FreeBSD compat, unused) */ #define FS_INDEXDIRS 0x008 /* kernel supports indexed directories */ #define FS_ACLS 0x010 /* filesystem has ACLs enabled */ #define FS_MULTILABEL 0x020 /* filesystem is MAC multi-label */ #define FS_GJOURNAL 0x40 /* gjournaled filesystem */ #define FS_FLAGS_UPDATED 0x80 /* flags have been moved to new location */ #define FS_DOWAPBL 0x100 /* Write ahead physical block logging */ /* Filesystem flags that are ok for NetBSD if set in fs_flags */ #define FS_KNOWN_FLAGS (FS_DOSOFTDEP | FS_DOWAPBL) /* * Filesystem internal flags, also in fs_flags. * (Pick highest number to avoid conflicts with others) */ #define FS_SWAPPED 0x80000000 /* filesystem is endian swapped */ #define FS_INTERNAL 0x80000000 /* mask for internal flags */ /* * Macros to access bits in the fs_active array. */ #define ACTIVECG_SET(fs, cg) \ do { \ if ((fs)->fs_active != NULL) \ setbit((fs)->fs_active, (cg)); \ } while (/*CONSTCOND*/ 0) #define ACTIVECG_CLR(fs, cg) \ do { \ if ((fs)->fs_active != NULL) \ clrbit((fs)->fs_active, (cg)); \ } while (/*CONSTCOND*/ 0) #define ACTIVECG_ISSET(fs, cg) \ ((fs)->fs_active != NULL && isset((fs)->fs_active, (cg))) /* * The size of a cylinder group is calculated by CGSIZE. The maximum size * is limited by the fact that cylinder groups are at most one block. * Its size is derived from the size of the maps maintained in the * cylinder group and the (struct cg) size. */ #define CGSIZE_IF(fs, ipg, fpg) \ /* base cg */ (sizeof(struct cg) + sizeof(int32_t) + \ /* old btotoff */ (fs)->fs_old_cpg * sizeof(int32_t) + \ /* old boff */ (fs)->fs_old_cpg * sizeof(u_int16_t) + \ /* inode map */ howmany((ipg), NBBY) + \ /* block map */ howmany((fpg), NBBY) +\ /* if present */ ((fs)->fs_contigsumsize <= 0 ? 0 : \ /* cluster sum */ (fs)->fs_contigsumsize * sizeof(int32_t) + \ /* cluster map */ howmany(fragstoblks(fs, (fpg)), NBBY))) #define CGSIZE(fs) CGSIZE_IF((fs), (fs)->fs_ipg, (fs)->fs_fpg) /* * The minimal number of cylinder groups that should be created. */ #define MINCYLGRPS 4 /* * Convert cylinder group to base address of its global summary info. */ #define fs_cs(fs, indx) fs_csp[indx] /* * Cylinder group block for a filesystem. */ #define CG_MAGIC 0x090255 struct cg { int32_t cg_firstfield; /* historic cyl groups linked list */ int32_t cg_magic; /* magic number */ int32_t cg_old_time; /* time last written */ int32_t cg_cgx; /* we are the cgx'th cylinder group */ int16_t cg_old_ncyl; /* number of cyl's this cg */ int16_t cg_old_niblk; /* number of inode blocks this cg */ int32_t cg_ndblk; /* number of data blocks this cg */ struct csum cg_cs; /* cylinder summary information */ int32_t cg_rotor; /* position of last used block */ int32_t cg_frotor; /* position of last used frag */ int32_t cg_irotor; /* position of last used inode */ int32_t cg_frsum[MAXFRAG]; /* counts of available frags */ int32_t cg_old_btotoff; /* (int32) block totals per cylinder */ int32_t cg_old_boff; /* (u_int16) free block positions */ int32_t cg_iusedoff; /* (u_int8) used inode map */ int32_t cg_freeoff; /* (u_int8) free block map */ int32_t cg_nextfreeoff; /* (u_int8) next available space */ int32_t cg_clustersumoff; /* (u_int32) counts of avail clusters */ int32_t cg_clusteroff; /* (u_int8) free cluster map */ int32_t cg_nclusterblks; /* number of clusters this cg */ int32_t cg_niblk; /* number of inode blocks this cg */ int32_t cg_initediblk; /* last initialized inode */ int32_t cg_sparecon32[3]; /* reserved for future use */ int64_t cg_time; /* time last written */ int64_t cg_sparecon64[3]; /* reserved for future use */ u_int8_t cg_space[1]; /* space for cylinder group maps */ /* actually longer */ }; /* * The following structure is defined * for compatibility with old filesystems. */ struct ocg { int32_t cg_firstfield; /* historic linked list of cyl groups */ int32_t cg_unused_1; /* used for incore cyl groups */ int32_t cg_time; /* time last written */ int32_t cg_cgx; /* we are the cgx'th cylinder group */ int16_t cg_ncyl; /* number of cyl's this cg */ int16_t cg_niblk; /* number of inode blocks this cg */ int32_t cg_ndblk; /* number of data blocks this cg */ struct csum cg_cs; /* cylinder summary information */ int32_t cg_rotor; /* position of last used block */ int32_t cg_frotor; /* position of last used frag */ int32_t cg_irotor; /* position of last used inode */ int32_t cg_frsum[8]; /* counts of available frags */ int32_t cg_btot[32]; /* block totals per cylinder */ int16_t cg_b[32][8]; /* positions of free blocks */ u_int8_t cg_iused[256]; /* used inode map */ int32_t cg_magic; /* magic number */ u_int8_t cg_free[1]; /* free block map */ /* actually longer */ }; /* * Macros for access to cylinder group array structures. */ #define old_cg_blktot_old(cgp, ns) \ (((struct ocg *)(cgp))->cg_btot) #define old_cg_blks_old(fs, cgp, cylno, ns) \ (((struct ocg *)(cgp))->cg_b[cylno]) #define old_cg_blktot_new(cgp, ns) \ ((int32_t *)((u_int8_t *)(cgp) + \ ufs_rw32((cgp)->cg_old_btotoff, (ns)))) #define old_cg_blks_new(fs, cgp, cylno, ns) \ ((int16_t *)((u_int8_t *)(cgp) + \ ufs_rw32((cgp)->cg_old_boff, (ns))) + (cylno) * (fs)->fs_old_nrpos) #define old_cg_blktot(cgp, ns) \ ((ufs_rw32((cgp)->cg_magic, (ns)) != CG_MAGIC) ? \ old_cg_blktot_old(cgp, ns) : old_cg_blktot_new(cgp, ns)) #define old_cg_blks(fs, cgp, cylno, ns) \ ((ufs_rw32((cgp)->cg_magic, (ns)) != CG_MAGIC) ? \ old_cg_blks_old(fs, cgp, cylno, ns) : old_cg_blks_new(fs, cgp, cylno, ns)) #define cg_inosused_new(cgp, ns) \ ((u_int8_t *)((u_int8_t *)(cgp) + \ ufs_rw32((cgp)->cg_iusedoff, (ns)))) #define cg_blksfree_new(cgp, ns) \ ((u_int8_t *)((u_int8_t *)(cgp) + \ ufs_rw32((cgp)->cg_freeoff, (ns)))) #define cg_chkmagic_new(cgp, ns) \ (ufs_rw32((cgp)->cg_magic, (ns)) == CG_MAGIC) #define cg_inosused_old(cgp, ns) \ (((struct ocg *)(cgp))->cg_iused) #define cg_blksfree_old(cgp, ns) \ (((struct ocg *)(cgp))->cg_free) #define cg_chkmagic_old(cgp, ns) \ (ufs_rw32(((struct ocg *)(cgp))->cg_magic, (ns)) == CG_MAGIC) #define cg_inosused(cgp, ns) \ ((ufs_rw32((cgp)->cg_magic, (ns)) != CG_MAGIC) ? \ cg_inosused_old(cgp, ns) : cg_inosused_new(cgp, ns)) #define cg_blksfree(cgp, ns) \ ((ufs_rw32((cgp)->cg_magic, (ns)) != CG_MAGIC) ? \ cg_blksfree_old(cgp, ns) : cg_blksfree_new(cgp, ns)) #define cg_chkmagic(cgp, ns) \ (cg_chkmagic_new(cgp, ns) || cg_chkmagic_old(cgp, ns)) #define cg_clustersfree(cgp, ns) \ ((u_int8_t *)((u_int8_t *)(cgp) + \ ufs_rw32((cgp)->cg_clusteroff, (ns)))) #define cg_clustersum(cgp, ns) \ ((int32_t *)((u_int8_t *)(cgp) + \ ufs_rw32((cgp)->cg_clustersumoff, (ns)))) /* * Turn filesystem block numbers into disk block addresses. * This maps filesystem blocks to device size blocks. */ #if defined (_KERNEL) #define fsbtodb(fs, b) ((b) << ((fs)->fs_fshift - DEV_BSHIFT)) #define dbtofsb(fs, b) ((b) >> ((fs)->fs_fshift - DEV_BSHIFT)) #else #define fsbtodb(fs, b) ((b) << (fs)->fs_fsbtodb) #define dbtofsb(fs, b) ((b) >> (fs)->fs_fsbtodb) #endif /* * Cylinder group macros to locate things in cylinder groups. * They calc filesystem addresses of cylinder group data structures. */ #define cgbase(fs, c) (((daddr_t)(fs)->fs_fpg) * (c)) #define cgstart_ufs1(fs, c) \ (cgbase(fs, c) + (fs)->fs_old_cgoffset * ((c) & ~((fs)->fs_old_cgmask))) #define cgstart_ufs2(fs, c) cgbase((fs), (c)) #define cgstart(fs, c) ((fs)->fs_magic == FS_UFS2_MAGIC \ ? cgstart_ufs2((fs), (c)) : cgstart_ufs1((fs), (c))) #define cgdmin(fs, c) (cgstart(fs, c) + (fs)->fs_dblkno) /* 1st data */ #define cgimin(fs, c) (cgstart(fs, c) + (fs)->fs_iblkno) /* inode blk */ #define cgsblock(fs, c) (cgstart(fs, c) + (fs)->fs_sblkno) /* super blk */ #define cgtod(fs, c) (cgstart(fs, c) + (fs)->fs_cblkno) /* cg block */ /* * Macros for handling inode numbers: * inode number to filesystem block offset. * inode number to cylinder group number. * inode number to filesystem block address. */ #define ino_to_cg(fs, x) ((x) / (fs)->fs_ipg) #define ino_to_fsba(fs, x) \ ((daddr_t)(cgimin(fs, ino_to_cg(fs, x)) + \ (blkstofrags((fs), (((x) % (fs)->fs_ipg) / INOPB(fs)))))) #define ino_to_fsbo(fs, x) ((x) % INOPB(fs)) /* * Give cylinder group number for a filesystem block. * Give cylinder group block number for a filesystem block. */ #define dtog(fs, d) ((d) / (fs)->fs_fpg) #define dtogd(fs, d) ((d) % (fs)->fs_fpg) /* * Extract the bits for a block from a map. * Compute the cylinder and rotational position of a cyl block addr. */ #define blkmap(fs, map, loc) \ (((map)[(loc) / NBBY] >> ((loc) % NBBY)) & (0xff >> (NBBY - (fs)->fs_frag))) #define old_cbtocylno(fs, bno) \ (fsbtodb(fs, bno) / (fs)->fs_old_spc) #define old_cbtorpos(fs, bno) \ ((fs)->fs_old_nrpos <= 1 ? 0 : \ (fsbtodb(fs, bno) % (fs)->fs_old_spc / (fs)->fs_old_nsect * (fs)->fs_old_trackskew + \ fsbtodb(fs, bno) % (fs)->fs_old_spc % (fs)->fs_old_nsect * (fs)->fs_old_interleave) % \ (fs)->fs_old_nsect * (fs)->fs_old_nrpos / (fs)->fs_old_npsect) /* * The following macros optimize certain frequently calculated * quantities by using shifts and masks in place of divisions * modulos and multiplications. */ #define blkoff(fs, loc) /* calculates (loc % fs->fs_bsize) */ \ ((loc) & (fs)->fs_qbmask) #define fragoff(fs, loc) /* calculates (loc % fs->fs_fsize) */ \ ((loc) & (fs)->fs_qfmask) #define lfragtosize(fs, frag) /* calculates ((off_t)frag * fs->fs_fsize) */ \ (((off_t)(frag)) << (fs)->fs_fshift) #define lblktosize(fs, blk) /* calculates ((off_t)blk * fs->fs_bsize) */ \ (((off_t)(blk)) << (fs)->fs_bshift) #define lblkno(fs, loc) /* calculates (loc / fs->fs_bsize) */ \ ((loc) >> (fs)->fs_bshift) #define numfrags(fs, loc) /* calculates (loc / fs->fs_fsize) */ \ ((loc) >> (fs)->fs_fshift) #define blkroundup(fs, size) /* calculates roundup(size, fs->fs_bsize) */ \ (((size) + (fs)->fs_qbmask) & (fs)->fs_bmask) #define fragroundup(fs, size) /* calculates roundup(size, fs->fs_fsize) */ \ (((size) + (fs)->fs_qfmask) & (fs)->fs_fmask) #define fragstoblks(fs, frags) /* calculates (frags / fs->fs_frag) */ \ ((frags) >> (fs)->fs_fragshift) #define blkstofrags(fs, blks) /* calculates (blks * fs->fs_frag) */ \ ((blks) << (fs)->fs_fragshift) #define fragnum(fs, fsb) /* calculates (fsb % fs->fs_frag) */ \ ((fsb) & ((fs)->fs_frag - 1)) #define blknum(fs, fsb) /* calculates rounddown(fsb, fs->fs_frag) */ \ ((fsb) &~ ((fs)->fs_frag - 1)) /* * Determine the number of available frags given a * percentage to hold in reserve. */ #define freespace(fs, percentreserved) \ (blkstofrags((fs), (fs)->fs_cstotal.cs_nbfree) + \ (fs)->fs_cstotal.cs_nffree - \ (((off_t)((fs)->fs_dsize)) * (percentreserved) / 100)) /* * Determining the size of a file block in the filesystem. */ #define blksize(fs, ip, lbn) \ (((lbn) >= NDADDR || (off_t)(ip)->i_size >= lblktosize(fs, (lbn) + 1)) \ ? (int32_t)(fs)->fs_bsize \ : (int32_t)(fragroundup(fs, blkoff(fs, (ip)->i_size)))) #define sblksize(fs, size, lbn) \ (((lbn) >= NDADDR || (size) >= ((lbn) + 1) << (fs)->fs_bshift) \ ? (fs)->fs_bsize \ : (fragroundup(fs, blkoff(fs, (size))))) /* * Number of inodes in a secondary storage block/fragment. */ #define INOPB(fs) ((fs)->fs_inopb) #define INOPF(fs) ((fs)->fs_inopb >> (fs)->fs_fragshift) /* * Number of indirects in a filesystem block. */ #define NINDIR(fs) ((fs)->fs_nindir) /* * Apple UFS Label: * We check for this to decide to use APPLEUFS_DIRBLKSIZ */ #define APPLEUFS_LABEL_MAGIC 0x4c41424c /* LABL */ #define APPLEUFS_LABEL_SIZE 1024 #define APPLEUFS_LABEL_OFFSET (BBSIZE - APPLEUFS_LABEL_SIZE) /* located at 7k */ #define APPLEUFS_LABEL_VERSION 1 #define APPLEUFS_MAX_LABEL_NAME 512 struct appleufslabel { u_int32_t ul_magic; u_int16_t ul_checksum; u_int16_t ul_unused0; u_int32_t ul_version; u_int32_t ul_time; u_int16_t ul_namelen; u_char ul_name[APPLEUFS_MAX_LABEL_NAME]; /* Warning: may not be null terminated */ u_int16_t ul_unused1; u_int64_t ul_uuid; /* Note this is only 4 byte aligned */ u_char ul_reserved[24]; u_char ul_unused[460]; } __packed; #endif /* !_UFS_FFS_FS_H_ */ makefs/src/usr.sbin/makefs/nbsrc/sys/ufs/ufs/dinode.h010064400000000000000000000166751314214553400200260ustar00/* $NetBSD: dinode.h,v 1.21 2009/06/28 09:26:18 ad Exp $ */ /* * 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) 1982, 1989, 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. * * @(#)dinode.h 8.9 (Berkeley) 3/29/95 */ /* * NOTE: COORDINATE ON-DISK FORMAT CHANGES WITH THE FREEBSD PROJECT. */ #ifndef _UFS_UFS_DINODE_H_ #define _UFS_UFS_DINODE_H_ /* * The root inode is the root of the filesystem. Inode 0 can't be used for * normal purposes and historically bad blocks were linked to inode 1, thus * the root inode is 2. (Inode 1 is no longer used for this purpose, however * numerous dump tapes make this assumption, so we are stuck with it). */ #define ROOTINO ((ino_t)2) /* * The Whiteout inode# is a dummy non-zero inode number which will * never be allocated to a real file. It is used as a place holder * in the directory entry which has been tagged as a DT_W entry. * See the comments about ROOTINO above. */ #define WINO ((ino_t)1) /* * A dinode contains all the meta-data associated with a UFS file. * This structure defines the on-disk format of a dinode. Since * this structure describes an on-disk structure, all its fields * are defined by types with precise widths. */ #define NXADDR 2 #define NDADDR 12 /* Direct addresses in inode. */ #define NIADDR 3 /* Indirect addresses in inode. */ struct ufs1_dinode { u_int16_t di_mode; /* 0: IFMT, permissions; see below. */ int16_t di_nlink; /* 2: File link count. */ union { u_int16_t oldids[2]; /* 4: Ffs: old user and group ids. */ u_int32_t inumber; /* 4: Lfs: inode number. */ } di_u; u_int64_t di_size; /* 8: File byte count. */ int32_t di_atime; /* 16: Last access time. */ int32_t di_atimensec; /* 20: Last access time. */ int32_t di_mtime; /* 24: Last modified time. */ int32_t di_mtimensec; /* 28: Last modified time. */ int32_t di_ctime; /* 32: Last inode change time. */ int32_t di_ctimensec; /* 36: Last inode change time. */ int32_t di_db[NDADDR]; /* 40: Direct disk blocks. */ int32_t di_ib[NIADDR]; /* 88: Indirect disk blocks. */ u_int32_t di_flags; /* 100: Status flags (chflags). */ u_int32_t di_blocks; /* 104: Blocks actually held. */ int32_t di_gen; /* 108: Generation number. */ u_int32_t di_uid; /* 112: File owner. */ u_int32_t di_gid; /* 116: File group. */ u_int64_t di_modrev; /* 120: i_modrev for NFSv4 */ }; struct ufs2_dinode { u_int16_t di_mode; /* 0: IFMT, permissions; see below. */ int16_t di_nlink; /* 2: File link count. */ u_int32_t di_uid; /* 4: File owner. */ u_int32_t di_gid; /* 8: File group. */ u_int32_t di_blksize; /* 12: Inode blocksize. */ u_int64_t di_size; /* 16: File byte count. */ u_int64_t di_blocks; /* 24: Bytes actually held. */ int64_t di_atime; /* 32: Last access time. */ int64_t di_mtime; /* 40: Last modified time. */ int64_t di_ctime; /* 48: Last inode change time. */ int64_t di_birthtime; /* 56: Inode creation time. */ int32_t di_mtimensec; /* 64: Last modified time. */ int32_t di_atimensec; /* 68: Last access time. */ int32_t di_ctimensec; /* 72: Last inode change time. */ int32_t di_birthnsec; /* 76: Inode creation time. */ int32_t di_gen; /* 80: Generation number. */ u_int32_t di_kernflags; /* 84: Kernel flags. */ u_int32_t di_flags; /* 88: Status flags (chflags). */ int32_t di_extsize; /* 92: External attributes block. */ int64_t di_extb[NXADDR];/* 96: External attributes block. */ int64_t di_db[NDADDR]; /* 112: Direct disk blocks. */ int64_t di_ib[NIADDR]; /* 208: Indirect disk blocks. */ u_int64_t di_modrev; /* 232: i_modrev for NFSv4 */ int64_t di_spare[2]; /* 240: Reserved; currently unused */ }; /* * The di_db fields may be overlaid with other information for * file types that do not have associated disk storage. Block * and character devices overlay the first data block with their * dev_t value. Short symbolic links place their path in the * di_db area. */ #define di_inumber di_u.inumber #define di_ogid di_u.oldids[1] #define di_ouid di_u.oldids[0] #define di_rdev di_db[0] #define MAXSYMLINKLEN_UFS1 ((NDADDR + NIADDR) * sizeof(int32_t)) #define MAXSYMLINKLEN_UFS2 ((NDADDR + NIADDR) * sizeof(int64_t)) #define MAXSYMLINKLEN(ip) \ ((ip)->i_ump->um_fstype == UFS1) ? \ MAXSYMLINKLEN_UFS1 : MAXSYMLINKLEN_UFS2 /* NeXT used to keep short symlinks in the inode even when using * FS_42INODEFMT. In that case fs->fs_maxsymlinklen is probably -1, * but short symlinks were stored in inodes shorter than this: */ #define APPLEUFS_MAXSYMLINKLEN 60 /* File permissions. */ #define IEXEC 0000100 /* Executable. */ #define IWRITE 0000200 /* Writable. */ #define IREAD 0000400 /* Readable. */ #define ISVTX 0001000 /* Sticky bit. */ #define ISGID 0002000 /* Set-gid. */ #define ISUID 0004000 /* Set-uid. */ /* File types. */ #define IFMT 0170000 /* Mask of file type. */ #define IFIFO 0010000 /* Named pipe (fifo). */ #define IFCHR 0020000 /* Character device. */ #define IFDIR 0040000 /* Directory file. */ #define IFBLK 0060000 /* Block device. */ #define IFREG 0100000 /* Regular file. */ #define IFLNK 0120000 /* Symbolic link. */ #define IFSOCK 0140000 /* UNIX domain socket. */ #define IFWHT 0160000 /* Whiteout. */ /* Size of the on-disk inode. */ #define DINODE1_SIZE (sizeof(struct ufs1_dinode)) /* 128 */ #define DINODE2_SIZE (sizeof(struct ufs2_dinode)) #endif /* !_UFS_UFS_DINODE_H_ */ makefs/src/usr.sbin/makefs/nbsrc/sys/ufs/ufs/dir.h010064400000000000000000000136151134456750000173340ustar00/* $NetBSD: dir.h,v 1.21 2009/07/22 04:49:19 dholland Exp $ */ /* * Copyright (c) 1982, 1986, 1989, 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. * * @(#)dir.h 8.5 (Berkeley) 4/27/95 */ #ifndef _UFS_UFS_DIR_H_ #define _UFS_UFS_DIR_H_ /* * Theoretically, directories can be more than 2Gb in length; however, in * practice this seems unlikely. So, we define the type doff_t as a 32-bit * quantity to keep down the cost of doing lookup on a 32-bit machine. */ #define doff_t int32_t #define MAXDIRSIZE (0x7fffffff) /* * A directory consists of some number of blocks of DIRBLKSIZ * bytes, where DIRBLKSIZ is chosen such that it can be transferred * to disk in a single atomic operation (e.g. 512 bytes on most machines). * * Each DIRBLKSIZ byte block contains some number of directory entry * structures, which are of variable length. Each directory entry has * a struct direct at the front of it, containing its inode number, * the length of the entry, and the length of the name contained in * the entry. These are followed by the name padded to a 4 byte boundary. * All names are guaranteed null terminated. * The maximum length of a name in a directory is FFS_MAXNAMLEN. * * The macro DIRSIZ(fmt, dp) gives the amount of space required to represent * a directory entry. Free space in a directory is represented by * entries which have dp->d_reclen > DIRSIZ(fmt, dp). All DIRBLKSIZ bytes * in a directory block are claimed by the directory entries. This * usually results in the last entry in a directory having a large * dp->d_reclen. When entries are deleted from a directory, the * space is returned to the previous entry in the same directory * block by increasing its dp->d_reclen. If the first entry of * a directory block is free, then its dp->d_ino is set to 0. * Entries other than the first in a directory do not normally have * dp->d_ino set to 0. */ #undef DIRBLKSIZ #define DIRBLKSIZ 512 #define FFS_MAXNAMLEN 255 #define APPLEUFS_DIRBLKSIZ 1024 #define d_ino d_fileno struct direct { u_int32_t d_fileno; /* inode number of entry */ u_int16_t d_reclen; /* length of this record */ u_int8_t d_type; /* file type, see below */ u_int8_t d_namlen; /* length of string in d_name */ char d_name[FFS_MAXNAMLEN + 1];/* name with length <= FFS_MAXNAMLEN */ }; /* * File types */ #define DT_UNKNOWN 0 #define DT_FIFO 1 #define DT_CHR 2 #define DT_DIR 4 #define DT_BLK 6 #define DT_REG 8 #define DT_LNK 10 #define DT_SOCK 12 #define DT_WHT 14 /* * Convert between stat structure types and directory types. */ #define IFTODT(mode) (((mode) & 0170000) >> 12) #define DTTOIF(dirtype) ((dirtype) << 12) /* * The DIRSIZ macro gives the minimum record length which will hold * the directory entry. This requires the amount of space in struct direct * without the d_name field, plus enough space for the name with a terminating * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary. */ #define DIRECTSIZ(namlen) \ ((sizeof(struct direct) - (FFS_MAXNAMLEN+1)) + (((namlen)+1 + 3) &~ 3)) #if (BYTE_ORDER == LITTLE_ENDIAN) #define DIRSIZ(oldfmt, dp, needswap) \ (((oldfmt) && !(needswap)) ? \ DIRECTSIZ((dp)->d_type) : DIRECTSIZ((dp)->d_namlen)) #else #define DIRSIZ(oldfmt, dp, needswap) \ (((oldfmt) && (needswap)) ? \ DIRECTSIZ((dp)->d_type) : DIRECTSIZ((dp)->d_namlen)) #endif #define OLDDIRFMT 1 #define NEWDIRFMT 0 /* * Template for manipulating directories. Should use struct direct's, * but the name field is FFS_MAXNAMLEN - 1, and this just won't do. */ struct dirtemplate { u_int32_t dot_ino; int16_t dot_reclen; u_int8_t dot_type; u_int8_t dot_namlen; char dot_name[4]; /* must be multiple of 4 */ u_int32_t dotdot_ino; int16_t dotdot_reclen; u_int8_t dotdot_type; u_int8_t dotdot_namlen; char dotdot_name[4]; /* ditto */ }; /* * This is the old format of directories, sanz type element. */ struct odirtemplate { u_int32_t dot_ino; int16_t dot_reclen; u_int16_t dot_namlen; char dot_name[4]; /* must be multiple of 4 */ u_int32_t dotdot_ino; int16_t dotdot_reclen; u_int16_t dotdot_namlen; char dotdot_name[4]; /* ditto */ }; #endif /* !_UFS_UFS_DIR_H_ */ makefs/src/usr.sbin/makefs/nbsrc/sys/ufs/ufs/ufs_bswap.h010064400000000000000000000055161341415246000205430ustar00/** $MirOS: src/usr.sbin/makefs/nbsrc/sys/ufs/ufs/ufs_bswap.h,v 1.9 2019/01/05 16:02:29 tg Exp $ */ /* $NetBSD: ufs_bswap.h,v 1.19 2009/10/19 18:41:17 bouyer Exp $ */ /* * Copyright (c) 2009, 2010 * Thorsten Glaser * Copyright (c) 1998 Manuel Bouyer. * * 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 _UFS_UFS_BSWAP_H_ #define _UFS_UFS_BSWAP_H_ #if defined(_KERNEL_OPT) #include "opt_ffs.h" #endif #ifdef __MirBSD__ #include #elif defined(__GLIBC__) #include #else #include #endif /* Macros to access UFS flags */ #ifdef FFS_EI #define UFS_MPNEEDSWAP(ump) ((ump)->um_flags & UFS_NEEDSWAP) #define UFS_FSNEEDSWAP(fs) ((fs)->fs_flags & FS_SWAPPED) #define UFS_IPNEEDSWAP(ip) UFS_MPNEEDSWAP((ip)->i_ump) #else #define UFS_MPNEEDSWAP(ump) (0) #define UFS_FSNEEDSWAP(fs) (0) #define UFS_IPNEEDSWAP(ip) (0) #endif #if !defined(_KERNEL) || defined(FFS_EI) #ifdef GNUPORT #include #endif /* inlines for access to swapped data */ static inline u_int16_t ufs_rw16(uint16_t a, int ns) { return ((ns) ? bswap16(a) : (a)); } static inline u_int32_t ufs_rw32(uint32_t a, int ns) { return ((ns) ? bswap32(a) : (a)); } static inline u_int64_t ufs_rw64(uint64_t a, int ns) { return ((ns) ? bswap64(a) : (a)); } #else #define ufs_rw16(a, ns) ((uint16_t)(a)) #define ufs_rw32(a, ns) ((uint32_t)(a)) #define ufs_rw64(a, ns) ((uint64_t)(a)) #endif #define ufs_add16(a, b, ns) \ (a) = ufs_rw16(ufs_rw16((a), (ns)) + (b), (ns)) #define ufs_add32(a, b, ns) \ (a) = ufs_rw32(ufs_rw32((a), (ns)) + (b), (ns)) #define ufs_add64(a, b, ns) \ (a) = ufs_rw64(ufs_rw64((a), (ns)) + (b), (ns)) #endif /* !_UFS_UFS_BSWAP_H_ */ makefs/src/usr.sbin/makefs/nbsrc/usr.sbin/mtree/extern.h010064400000000000000000000060211341415246000205160ustar00/** $MirOS: src/usr.sbin/makefs/nbsrc/usr.sbin/mtree/extern.h,v 1.9 2019/01/05 16:02:29 tg Exp $ */ /* $NetBSD: extern.h,v 1.31 2009/04/08 19:03:13 apb Exp $ */ /*- * Copyright (c) 2009 * Thorsten Glaser * Copyright (c) 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. * * @(#)extern.h 8.1 (Berkeley) 6/6/93 */ #include "mtree.h" #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #elif !defined(GNUPORT) #define HAVE_STRUCT_STAT_ST_FLAGS 1 #endif #include #include #ifndef GNUPORT #include #endif #if HAVE_NETDB_H /* For MAXHOSTNAMELEN on some platforms. */ #include #endif #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 256 #endif void addtag(slist_t *, char *); int check_excludes(const char *, const char *); int compare(NODE *, FTSENT *); int crc(int, u_int32_t *, u_int32_t *); void cwalk(void); void dump_nodes(const char *, NODE *, int); void init_excludes(void); int matchtags(NODE *); void mtree_err(const char *, ...) __dead __attribute__((__format__(__printf__, 1, 2))); const char *nodetype(u_int); u_int parsekey(const char *, int *); void parsetags(slist_t *, char *); u_int parsetype(const char *); void read_excludes_file(const char *); const char *rlink(const char *); int verify(void); extern int dflag, eflag, iflag, lflag, mflag, rflag, sflag, tflag, uflag; extern int mtree_Mflag, mtree_Sflag, mtree_Wflag; extern size_t mtree_lineno; extern u_int32_t crc_total; extern int ftsoptions, keys; extern char fullpath[]; extern slist_t includetags, excludetags; makefs/src/usr.sbin/makefs/nbsrc/usr.sbin/mtree/getid.c010064400000000000000000000246361341415246000203140ustar00/** $MirOS: src/usr.sbin/makefs/nbsrc/usr.sbin/mtree/getid.c,v 1.9 2019/01/05 16:02:29 tg Exp $ */ /* $NetBSD: getid.c,v 1.7 2008/04/28 20:24:17 martin Exp $ */ /* from: NetBSD: getpwent.c,v 1.48 2000/10/03 03:22:26 enami Exp */ /* from: NetBSD: getgrent.c,v 1.41 2002/01/12 23:51:30 lukem Exp */ /* * Copyright (c) 2009 * Thorsten Glaser * Copyright (c) 1987, 1988, 1989, 1993, 1994, 1995 * 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. */ /*- * Copyright (c) 2002 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Luke Mewburn of Wasabi Systems. * * 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. */ #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #include __RCSID("$NetBSD: getid.c,v 1.7 2008/04/28 20:24:17 martin Exp $"); __IDSTRING(mbsdid, "$MirOS: src/usr.sbin/makefs/nbsrc/usr.sbin/mtree/getid.c,v 1.9 2019/01/05 16:02:29 tg Exp $"); #include #include #include #include #include #include #include #include #include #include "extern.h" #if defined(__MirBSD__) || defined(GNUPORT) #include "pwcache.h" #endif /* XXX eglibc? */ #ifndef UID_MAX #define UID_MAX UINT_MAX /* max value for a uid_t */ #endif #ifndef GID_MAX #define GID_MAX UINT_MAX /* max value for a gid_t */ #endif static struct group * gi_getgrnam(const char *); static struct group * gi_getgrgid(gid_t); static int gi_setgroupent(int); static void gi_endgrent(void); static int grstart(void); static int grscan(int, gid_t, const char *); static int grmatchline(int, gid_t, const char *); static struct passwd * gi_getpwnam(const char *); static struct passwd * gi_getpwuid(uid_t); static int gi_setpassent(int); static void gi_endpwent(void); static int pwstart(void); static int pwscan(int, uid_t, const char *); static int pwmatchline(int, uid_t, const char *); #define MAXGRP 200 #define MAXLINELENGTH 1024 static FILE *_gr_fp; static struct group _gr_group; static int _gr_stayopen; static int _gr_filesdone; static FILE *_pw_fp; static struct passwd _pw_passwd; /* password structure */ static int _pw_stayopen; /* keep fd's open */ static int _pw_filesdone; static char *grfilep = NULL; static char *pwfilep = NULL; static char *members[MAXGRP]; static char grline[MAXLINELENGTH]; static char pwline[MAXLINELENGTH]; int setup_getid(const char *dir) { if (dir == NULL) return (0); /* close existing databases */ gi_endgrent(); gi_endpwent(); /* build paths to new databases */ asprintf(&grfilep, "%s/group", dir); asprintf(&pwfilep, "%s/master.passwd", dir); /* try to open new databases */ if (!grstart() || !pwstart()) return (0); /* switch pwcache(3) lookup functions */ if (pwcache_groupdb(gi_setgroupent, gi_endgrent, gi_getgrnam, gi_getgrgid) == -1 || pwcache_userdb(gi_setpassent, gi_endpwent, gi_getpwnam, gi_getpwuid) == -1) return (0); return (1); } /* * group lookup functions */ static struct group * gi_getgrnam(const char *name) { int rval; if (!grstart()) return NULL; rval = grscan(1, 0, name); if (!_gr_stayopen) endgrent(); return (rval) ? &_gr_group : NULL; } static struct group * gi_getgrgid(gid_t gid) { int rval; if (!grstart()) return NULL; rval = grscan(1, gid, NULL); if (!_gr_stayopen) endgrent(); return (rval) ? &_gr_group : NULL; } static int gi_setgroupent(int stayopen) { if (!grstart()) return 0; _gr_stayopen = stayopen; return 1; } static void gi_endgrent(void) { _gr_filesdone = 0; if (_gr_fp) { (void)fclose(_gr_fp); _gr_fp = NULL; } } static int grstart(void) { _gr_filesdone = 0; if (_gr_fp) { rewind(_gr_fp); return 1; } if (!grfilep) /* sanity check */ return 0; return (_gr_fp = fopen(grfilep, "r")) ? 1 : 0; } static int grscan(int search, gid_t gid, const char *name) { if (_gr_filesdone) return 0; for (;;) { if (!fgets(grline, sizeof(grline), _gr_fp)) { if (!search) _gr_filesdone = 1; return 0; } /* skip lines that are too big */ if (!strchr(grline, '\n')) { int ch; while ((ch = getc(_gr_fp)) != '\n' && ch != EOF) ; continue; } if (grmatchline(search, gid, name)) return 1; } /* NOTREACHED */ } static int grmatchline(int search, gid_t gid, const char *name) { unsigned long id; char **m; char *cp, *bp, *ep; /* name may be NULL if search is nonzero */ bp = grline; memset(&_gr_group, 0, sizeof(_gr_group)); _gr_group.gr_name = strsep(&bp, ":\n"); if (search && name && strcmp(_gr_group.gr_name, name)) return 0; _gr_group.gr_passwd = strsep(&bp, ":\n"); if (!(cp = strsep(&bp, ":\n"))) return 0; id = strtoul(cp, &ep, 10); if (id > GID_MAX || *ep != '\0') return 0; _gr_group.gr_gid = (gid_t)id; if (search && name == NULL && _gr_group.gr_gid != gid) return 0; cp = NULL; if (bp == NULL) return 0; for (_gr_group.gr_mem = m = 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; } /* * user lookup functions */ static struct passwd * gi_getpwnam(const char *name) { int rval; if (!pwstart()) return NULL; rval = pwscan(1, 0, name); if (!_pw_stayopen) endpwent(); return (rval) ? &_pw_passwd : NULL; } static struct passwd * gi_getpwuid(uid_t uid) { int rval; if (!pwstart()) return NULL; rval = pwscan(1, uid, NULL); if (!_pw_stayopen) endpwent(); return (rval) ? &_pw_passwd : NULL; } static int gi_setpassent(int stayopen) { if (!pwstart()) return 0; _pw_stayopen = stayopen; return 1; } static void gi_endpwent(void) { _pw_filesdone = 0; if (_pw_fp) { (void)fclose(_pw_fp); _pw_fp = NULL; } } static int pwstart(void) { _pw_filesdone = 0; if (_pw_fp) { rewind(_pw_fp); return 1; } if (!pwfilep) /* sanity check */ return 0; return (_pw_fp = fopen(pwfilep, "r")) ? 1 : 0; } static int pwscan(int search, uid_t uid, const char *name) { if (_pw_filesdone) return 0; for (;;) { if (!fgets(pwline, sizeof(pwline), _pw_fp)) { if (!search) _pw_filesdone = 1; return 0; } /* skip lines that are too big */ if (!strchr(pwline, '\n')) { int ch; while ((ch = getc(_pw_fp)) != '\n' && ch != EOF) ; continue; } if (pwmatchline(search, uid, name)) return 1; } /* NOTREACHED */ } static int pwmatchline(int search, uid_t uid, const char *name) { unsigned long id; char *cp, *bp, *ep; /* name may be NULL if search is nonzero */ bp = pwline; memset(&_pw_passwd, 0, sizeof(_pw_passwd)); _pw_passwd.pw_name = strsep(&bp, ":\n"); /* name */ if (search && name && strcmp(_pw_passwd.pw_name, name)) return 0; _pw_passwd.pw_passwd = strsep(&bp, ":\n"); /* passwd */ if (!(cp = strsep(&bp, ":\n"))) /* uid */ return 0; id = strtoul(cp, &ep, 10); if (id > UID_MAX || *ep != '\0') return 0; _pw_passwd.pw_uid = (uid_t)id; if (search && name == NULL && _pw_passwd.pw_uid != uid) return 0; if (!(cp = strsep(&bp, ":\n"))) /* gid */ return 0; id = strtoul(cp, &ep, 10); if (id > GID_MAX || *ep != '\0') return 0; _pw_passwd.pw_gid = (gid_t)id; if (!(ep = strsep(&bp, ":"))) /* class */ return 0; if (!(ep = strsep(&bp, ":"))) /* change */ return 0; if (!(ep = strsep(&bp, ":"))) /* expire */ return 0; if (!(_pw_passwd.pw_gecos = strsep(&bp, ":\n"))) /* gecos */ return 0; if (!(_pw_passwd.pw_dir = strsep(&bp, ":\n"))) /* directory */ return 0; if (!(_pw_passwd.pw_shell = strsep(&bp, ":\n"))) /* shell */ return 0; if (strchr(bp, ':') != NULL) return 0; return 1; } makefs/src/usr.sbin/makefs/nbsrc/usr.sbin/mtree/misc.c010064400000000000000000000152761134453672200201620ustar00/* $NetBSD: misc.c,v 1.30 2009/01/18 12:09:38 lukem Exp $ */ /*- * Copyright (c) 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. * * @(#)misc.c 8.1 (Berkeley) 6/6/93 */ #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #include #if defined(__RCSID) && !defined(lint) __RCSID("$NetBSD: misc.c,v 1.30 2009/01/18 12:09:38 lukem Exp $"); #endif /* not lint */ #include #include #include #include #include #include #include "extern.h" typedef struct _key { const char *name; /* key name */ u_int val; /* value */ #define NEEDVALUE 0x01 u_int flags; } KEY; /* NB: the following tables must be sorted lexically. */ static KEY keylist[] = { {"cksum", F_CKSUM, NEEDVALUE}, {"device", F_DEV, NEEDVALUE}, {"flags", F_FLAGS, NEEDVALUE}, {"gid", F_GID, NEEDVALUE}, {"gname", F_GNAME, NEEDVALUE}, {"ignore", F_IGN, 0}, {"link", F_SLINK, NEEDVALUE}, {"md5", F_MD5, NEEDVALUE}, {"md5digest", F_MD5, NEEDVALUE}, {"mode", F_MODE, NEEDVALUE}, {"nlink", F_NLINK, NEEDVALUE}, {"optional", F_OPT, 0}, {"rmd160", F_RMD160, NEEDVALUE}, {"rmd160digest",F_RMD160, NEEDVALUE}, {"sha1", F_SHA1, NEEDVALUE}, {"sha1digest", F_SHA1, NEEDVALUE}, {"sha256", F_SHA256, NEEDVALUE}, {"sha256digest",F_SHA256, NEEDVALUE}, {"sha384", F_SHA384, NEEDVALUE}, {"sha384digest",F_SHA384, NEEDVALUE}, {"sha512", F_SHA512, NEEDVALUE}, {"sha512digest",F_SHA512, NEEDVALUE}, {"size", F_SIZE, NEEDVALUE}, {"tags", F_TAGS, NEEDVALUE}, {"time", F_TIME, NEEDVALUE}, {"type", F_TYPE, NEEDVALUE}, {"uid", F_UID, NEEDVALUE}, {"uname", F_UNAME, NEEDVALUE} }; static KEY typelist[] = { {"block", F_BLOCK, 0}, {"char", F_CHAR, 0}, {"dir", F_DIR, 0}, #ifdef S_IFDOOR {"door", F_DOOR, 0}, #endif {"fifo", F_FIFO, 0}, {"file", F_FILE, 0}, {"link", F_LINK, 0}, {"socket", F_SOCK, 0}, }; slist_t excludetags, includetags; int keys = KEYDEFAULT; int keycompare(const void *, const void *); u_int parsekey(const char *name, int *needvaluep) { static int allbits; KEY *k, tmp; if (allbits == 0) { size_t i; for (i = 0; i < sizeof(keylist) / sizeof(KEY); i++) allbits |= keylist[i].val; } tmp.name = name; if (strcmp(name, "all") == 0) return (allbits); k = (KEY *)bsearch(&tmp, keylist, sizeof(keylist) / sizeof(KEY), sizeof(KEY), keycompare); if (k == NULL) mtree_err("unknown keyword `%s'", name); if (needvaluep) *needvaluep = k->flags & NEEDVALUE ? 1 : 0; return (k->val); } u_int parsetype(const char *name) { KEY *k, tmp; tmp.name = name; k = (KEY *)bsearch(&tmp, typelist, sizeof(typelist) / sizeof(KEY), sizeof(KEY), keycompare); if (k == NULL) mtree_err("unknown file type `%s'", name); return (k->val); } int keycompare(const void *a, const void *b) { return (strcmp(((const KEY *)a)->name, ((const KEY *)b)->name)); } void mtree_err(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vwarnx(fmt, ap); va_end(ap); if (mtree_lineno) warnx("failed at line %lu of the specification", (u_long) mtree_lineno); exit(1); /* NOTREACHED */ } void addtag(slist_t *list, char *elem) { #define TAG_CHUNK 20 if ((list->count % TAG_CHUNK) == 0) { char **new; new = (char **)realloc(list->list, (list->count + TAG_CHUNK) * sizeof(char *)); if (new == NULL) mtree_err("memory allocation error"); list->list = new; } list->list[list->count] = elem; list->count++; } void parsetags(slist_t *list, char *args) { char *p, *e; int len; if (args == NULL) { addtag(list, NULL); return; } while ((p = strsep(&args, ",")) != NULL) { if (*p == '\0') continue; len = strlen(p) + 3; /* "," + p + ",\0" */ if ((e = malloc(len)) == NULL) mtree_err("memory allocation error"); snprintf(e, len, ",%s,", p); addtag(list, e); } } /* * matchtags * returns 0 if there's a match from the exclude list in the node's tags, * or there's an include list and no match. * return 1 otherwise. */ int matchtags(NODE *node) { int i; if (node->tags) { for (i = 0; i < excludetags.count; i++) if (strstr(node->tags, excludetags.list[i])) break; if (i < excludetags.count) return (0); for (i = 0; i < includetags.count; i++) if (strstr(node->tags, includetags.list[i])) break; if (i > 0 && i == includetags.count) return (0); } else if (includetags.count > 0) { return (0); } return (1); } u_int nodetoino(u_int type) { switch (type) { case F_BLOCK: return S_IFBLK; case F_CHAR: return S_IFCHR; case F_DIR: return S_IFDIR; case F_FIFO: return S_IFIFO; case F_FILE: return S_IFREG; case F_LINK: return S_IFLNK; #ifdef S_IFSOCK case F_SOCK: return S_IFSOCK; #endif default: printf("unknown type %d", type); abort(); } /* NOTREACHED */ } const char * nodetype(u_int type) { return (inotype(nodetoino(type))); } const char * inotype(u_int type) { switch (type & S_IFMT) { case S_IFBLK: return ("block"); case S_IFCHR: return ("char"); case S_IFDIR: return ("dir"); case S_IFIFO: return ("fifo"); case S_IFREG: return ("file"); case S_IFLNK: return ("link"); #ifdef S_IFSOCK case S_IFSOCK: return ("socket"); #endif #ifdef S_IFDOOR case S_IFDOOR: return ("door"); #endif default: return ("unknown"); } /* NOTREACHED */ } makefs/src/usr.sbin/makefs/nbsrc/usr.sbin/mtree/mtree.h010064400000000000000000000123471134453672300203450ustar00/* $NetBSD: mtree.h,v 1.27 2009/04/04 21:49:49 apb Exp $ */ /*- * Copyright (c) 1990, 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. * * @(#)mtree.h 8.1 (Berkeley) 6/6/93 */ #ifndef _MTREE_H_ #define _MTREE_H_ #define KEYDEFAULT (F_GID | F_MODE | F_NLINK | F_SIZE | F_SLINK | \ F_TIME | F_TYPE | F_UID | F_FLAGS) #define MISMATCHEXIT 2 typedef struct _node { struct _node *parent, *child; /* up, down */ struct _node *prev, *next; /* left, right */ off_t st_size; /* size */ struct timespec st_mtimespec; /* last modification time */ char *slink; /* symbolic link reference */ uid_t st_uid; /* uid */ gid_t st_gid; /* gid */ #define MBITS (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO) mode_t st_mode; /* mode */ dev_t st_rdev; /* device type */ u_long st_flags; /* flags */ nlink_t st_nlink; /* link count */ u_long cksum; /* check sum */ char *md5digest; /* MD5 digest */ char *rmd160digest; /* RMD-160 digest */ char *sha1digest; /* SHA1 digest */ char *sha256digest; /* SHA256 digest */ char *sha384digest; /* SHA384 digest */ char *sha512digest; /* SHA512 digest */ char *tags; /* tags, comma delimited, * also with leading and * trailing commas */ size_t lineno; /* line # entry came from */ #define F_CKSUM 0x00000001 /* cksum(1) check sum */ #define F_DEV 0x00000002 /* device type */ #define F_DONE 0x00000004 /* directory done */ #define F_FLAGS 0x00000008 /* file flags */ #define F_GID 0x00000010 /* gid */ #define F_GNAME 0x00000020 /* group name */ #define F_IGN 0x00000040 /* ignore */ #define F_MAGIC 0x00000080 /* name has magic chars */ #define F_MD5 0x00000100 /* MD5 digest */ #define F_MODE 0x00000200 /* mode */ #define F_NLINK 0x00000400 /* number of links */ #define F_OPT 0x00000800 /* existence optional */ #define F_RMD160 0x00001000 /* RMD-160 digest */ #define F_SHA1 0x00002000 /* SHA1 digest */ #define F_SIZE 0x00004000 /* size */ #define F_SLINK 0x00008000 /* symbolic link */ #define F_TAGS 0x00010000 /* tags */ #define F_TIME 0x00020000 /* modification time */ #define F_TYPE 0x00040000 /* file type */ #define F_UID 0x00080000 /* uid */ #define F_UNAME 0x00100000 /* user name */ #define F_VISIT 0x00200000 /* file visited */ #define F_SHA256 0x00800000 /* SHA256 digest */ #define F_SHA384 0x01000000 /* SHA384 digest */ #define F_SHA512 0x02000000 /* SHA512 digest */ int flags; /* items set */ #define F_BLOCK 0x001 /* block special */ #define F_CHAR 0x002 /* char special */ #define F_DIR 0x004 /* directory */ #define F_FIFO 0x008 /* fifo */ #define F_FILE 0x010 /* regular file */ #define F_LINK 0x020 /* symbolic link */ #define F_SOCK 0x040 /* socket */ #define F_DOOR 0x080 /* door */ int type; /* file type */ char name[1]; /* file name (must be last) */ } NODE; typedef struct { char **list; int count; } slist_t; /* * prototypes for functions published to other programs which want to use * the specfile parser but don't want to pull in all of "extern.h" */ const char *inotype(u_int); u_int nodetoino(u_int); int setup_getid(const char *); NODE *spec(FILE *); void free_nodes(NODE *); char *vispath(const char *); #define RP(p) \ ((p)->fts_path[0] == '.' && (p)->fts_path[1] == '/' ? \ (p)->fts_path + 2 : (p)->fts_path) #define UF_MASK ((UF_NODUMP | UF_IMMUTABLE | \ UF_APPEND | UF_OPAQUE) \ & UF_SETTABLE) /* user settable flags */ #define SF_MASK ((SF_ARCHIVED | SF_IMMUTABLE | \ SF_APPEND) & SF_SETTABLE) /* root settable flags */ #define CH_MASK (UF_MASK | SF_MASK) /* all settable flags */ #define SP_FLGS (SF_IMMUTABLE | SF_APPEND) /* special flags */ #endif /* _MTREE_H_ */ makefs/src/usr.sbin/makefs/nbsrc/usr.sbin/mtree/spec.c010064400000000000000000000547131341415673200201570ustar00/** $MirOS: src/usr.sbin/makefs/nbsrc/usr.sbin/mtree/spec.c,v 1.14 2019/01/05 16:39:27 tg Exp $ */ /* $NetBSD: spec.c,v 1.78 2009/09/22 04:38:21 apb Exp $ */ /*- * Copyright (c) 2009, 2010 * Thorsten Glaser * 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. */ /*- * Copyright (c) 2001-2004 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Luke Mewburn of Wasabi Systems. * * 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. */ #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #ifdef GNUPORT #include #define NEED_FPARSELN_DECL #endif #if defined(__MirBSD__) || defined(GNUPORT) #include "mbsdtree.h" #endif #include #if defined(__RCSID) && !defined(lint) #if 0 static char sccsid[] = "@(#)spec.c 8.2 (Berkeley) 4/28/95"; #else __IDSTRING(mbsdid, "$MirOS: src/usr.sbin/makefs/nbsrc/usr.sbin/mtree/spec.c,v 1.14 2019/01/05 16:39:27 tg Exp $"); __RCSID("$NetBSD: spec.c,v 1.78 2009/09/22 04:38:21 apb Exp $"); #endif #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef GNUPORT #include #endif #include "extern.h" #include "pack_dev.h" #if defined(__MirBSD__) || defined(GNUPORT) #include "pwcache.h" #endif #ifdef GNUPORT extern mode_t getmode(const void *, mode_t); extern void *setmode(const char *); #endif size_t mtree_lineno; /* Current spec line number */ int mtree_Mflag; /* Merge duplicate entries */ int mtree_Wflag; /* Don't "whack" permissions */ int mtree_Sflag; /* Sort entries */ static dev_t parsedev(char *); static void replacenode(NODE *, NODE *); static void set(char *, NODE *); static void unset(char *, NODE *); static void addchild(NODE *, NODE *); static int nodecmp(const NODE *, const NODE *); #ifndef GNUPORT static int appendfield(int, const char *, ...) __attribute__((__format__(__printf__, 2, 3))); #endif #define REPLACEPTR(x,v) do { if ((x)) free((x)); (x) = (v); } while (0) NODE * spec(FILE *fp) { NODE *centry, *last, *pathparent, *cur; char *p, *e, *next; NODE ginfo, *root; char *buf, *tname, *ntname; size_t tnamelen, plen; root = NULL; centry = last = NULL; tname = NULL; tnamelen = 0; memset(&ginfo, 0, sizeof(ginfo)); for (mtree_lineno = 0; (buf = fparseln(fp, NULL, &mtree_lineno, NULL, FPARSELN_UNESCCOMM)); free(buf)) { /* Skip leading whitespace. */ for (p = buf; *p && isspace((unsigned char)*p); ++p) continue; /* If nothing but whitespace, continue. */ if (!*p) continue; #ifdef DEBUG fprintf(stderr, "line %lu: {%s}\n", (u_long)mtree_lineno, p); #endif /* Grab file name, "$", "set", or "unset". */ next = buf; while ((p = strsep(&next, " \t")) != NULL && *p == '\0') continue; if (p == NULL) mtree_err("missing field"); if (p[0] == '/') { if (strcmp(p + 1, "set") == 0) set(next, &ginfo); else if (strcmp(p + 1, "unset") == 0) unset(next, &ginfo); else mtree_err("invalid specification `%s'", p); continue; } if (strcmp(p, "..") == 0) { /* Don't go up, if haven't gone down. */ if (root == NULL) goto noparent; if (last->type != F_DIR || last->flags & F_DONE) { if (last == root) goto noparent; last = last->parent; } last->flags |= F_DONE; continue; noparent: mtree_err("no parent node"); } plen = strlen(p) + 1; if (plen > tnamelen) { if ((ntname = realloc(tname, plen)) == NULL) mtree_err("realloc: %s", strerror(errno)); tname = ntname; tnamelen = plen; } if (strunvis(tname, p) == -1) mtree_err("strunvis failed on `%s'", p); p = tname; pathparent = NULL; if (strchr(p, '/') != NULL) { cur = root; for (; (e = strchr(p, '/')) != NULL; p = e+1) { if (p == e) continue; /* handle // */ *e = '\0'; if (strcmp(p, ".") != 0) { while (cur && strcmp(cur->name, p) != 0) { cur = cur->next; } } if (cur == NULL || cur->type != F_DIR) { mtree_err("%s: %s", tname, "missing directory in specification"); } *e = '/'; pathparent = cur; cur = cur->child; } if (*p == '\0') mtree_err("%s: empty leaf element", tname); } if ((centry = calloc(1, sizeof(NODE) + strlen(p) + 1)) == NULL) mtree_err("%s", strerror(errno)); *centry = ginfo; centry->lineno = mtree_lineno; memcpy(centry->name, p, strlen(p)); centry->name[strlen(p)] = '\0'; #define MAGIC "?*[" if (strpbrk(p, MAGIC)) centry->flags |= F_MAGIC; set(next, centry); if (root == NULL) { /* * empty tree */ if (strcmp(centry->name, ".") != 0 || centry->type != F_DIR) mtree_err( "root node must be the directory `.'"); last = root = centry; root->parent = root; } else if (pathparent != NULL) { /* * full path entry; add or replace */ centry->parent = pathparent; addchild(pathparent, centry); last = centry; } else if (strcmp(centry->name, ".") == 0) { /* * duplicate "." entry; always replace */ replacenode(root, centry); } else if (last->type == F_DIR && !(last->flags & F_DONE)) { /* * new relative child in current dir; * add or replace */ centry->parent = last; addchild(last, centry); last = centry; } else { /* * new relative child in parent dir * (after encountering ".." entry); * add or replace */ centry->parent = last->parent; addchild(last->parent, centry); last = centry; } } return (root); } void free_nodes(NODE *root) { NODE *cur, *next; if (root == NULL) return; next = NULL; for (cur = root; cur != NULL; cur = next) { next = cur->next; free_nodes(cur->child); REPLACEPTR(cur->slink, NULL); REPLACEPTR(cur->md5digest, NULL); REPLACEPTR(cur->rmd160digest, NULL); REPLACEPTR(cur->sha1digest, NULL); REPLACEPTR(cur->sha256digest, NULL); REPLACEPTR(cur->sha384digest, NULL); REPLACEPTR(cur->sha512digest, NULL); REPLACEPTR(cur->tags, NULL); REPLACEPTR(cur, NULL); } } #ifndef GNUPORT /* * appendfield -- * Like printf(), but output a space either before or after * the regular output, according to the pathlast flag. */ static int appendfield(int pathlast, const char *fmt, ...) { va_list ap; int result; va_start(ap, fmt); if (!pathlast) printf(" "); result = vprintf(fmt, ap); if (pathlast) printf(" "); va_end(ap); return result; } /* * dump_nodes -- * dump the NODEs from `cur', based in the directory `dir'. * if pathlast is none zero, print the path last, otherwise print * it first. */ void dump_nodes(const char *dir, NODE *root, int pathlast) { NODE *cur; char path[MAXPATHLEN]; const char *name; char *str; char *p, *q; for (cur = root; cur != NULL; cur = cur->next) { if (cur->type != F_DIR && !matchtags(cur)) continue; if ((size_t)snprintf(path, sizeof(path), "%s%s%s", dir, *dir ? "/" : "", cur->name) >= (int)sizeof(path)) mtree_err("Pathname too long."); if (!pathlast) printf("%s", vispath(path)); #define MATCHFLAG(f) ((keys & (f)) && (cur->flags & (f))) if (MATCHFLAG(F_TYPE)) appendfield(pathlast, "type=%s", nodetype(cur->type)); if (MATCHFLAG(F_UID | F_UNAME)) { if (keys & F_UNAME && (name = user_from_uid(cur->st_uid, 1)) != NULL) appendfield(pathlast, "uname=%s", name); else appendfield(pathlast, "uid=%u", cur->st_uid); } if (MATCHFLAG(F_GID | F_GNAME)) { if (keys & F_GNAME && (name = group_from_gid(cur->st_gid, 1)) != NULL) appendfield(pathlast, "gname=%s", name); else appendfield(pathlast, "gid=%u", cur->st_gid); } if (MATCHFLAG(F_MODE)) appendfield(pathlast, "mode=%#o", cur->st_mode); if (MATCHFLAG(F_DEV) && (cur->type == F_BLOCK || cur->type == F_CHAR)) appendfield(pathlast, "device=%#llx", (long long)cur->st_rdev); if (MATCHFLAG(F_NLINK)) appendfield(pathlast, "nlink=%d", cur->st_nlink); if (MATCHFLAG(F_SLINK)) appendfield(pathlast, "link=%s", vispath(cur->slink)); if (MATCHFLAG(F_SIZE)) appendfield(pathlast, "size=%lld", (long long)cur->st_size); if (MATCHFLAG(F_TIME)) appendfield(pathlast, "time=%lld.%ld ", (long long)cur->st_mtimespec.tv_sec, cur->st_mtimespec.tv_nsec); if (MATCHFLAG(F_CKSUM)) appendfield(pathlast, "cksum=%lu", cur->cksum); if (MATCHFLAG(F_MD5)) appendfield(pathlast, "md5=%s", cur->md5digest); if (MATCHFLAG(F_RMD160)) appendfield(pathlast, "rmd160=%s", cur->rmd160digest); if (MATCHFLAG(F_SHA1)) appendfield(pathlast, "sha1=%s", cur->sha1digest); if (MATCHFLAG(F_SHA256)) appendfield(pathlast, "sha256=%s", cur->sha256digest); if (MATCHFLAG(F_SHA384)) appendfield(pathlast, "sha384=%s", cur->sha384digest); if (MATCHFLAG(F_SHA512)) appendfield(pathlast, "sha512=%s", cur->sha512digest); if (MATCHFLAG(F_FLAGS)) { str = flags_to_string(cur->st_flags, "none"); appendfield(pathlast, "flags=%s", str); free(str); } if (MATCHFLAG(F_IGN)) appendfield(pathlast, "ignore"); if (MATCHFLAG(F_OPT)) appendfield(pathlast, "optional"); if (MATCHFLAG(F_TAGS)) { /* don't output leading or trailing commas */ p = cur->tags; while (*p == ',') p++; q = p + strlen(p); while(q > p && q[-1] == ',') q--; appendfield(pathlast, "tags=%.*s", (int)(q - p), p); } puts(pathlast ? vispath(path) : ""); if (cur->child) dump_nodes(path, cur->child, pathlast); } } /* * vispath -- * strsvis(3) encodes path, which must not be longer than MAXPATHLEN * characters long, and returns a pointer to a static buffer containing * the result. */ char * vispath(const char *path) { const char extra[] = { ' ', '\t', '\n', '\\', '#', '\0' }; static char pathbuf[4*MAXPATHLEN + 1]; strsvis(pathbuf, path, VIS_CSTYLE, extra); return(pathbuf); } #endif /* ndef GNUPORT */ static dev_t parsedev(char *arg) { #define MAX_PACK_ARGS 3 u_long numbers[MAX_PACK_ARGS]; char *p, *ep, *dev; int argc; pack_t *pack; dev_t result; const char *error = NULL; if ((dev = strchr(arg, ',')) != NULL) { *dev++='\0'; if ((pack = pack_find(arg)) == NULL) mtree_err("unknown format `%s'", arg); argc = 0; while ((p = strsep(&dev, ",")) != NULL) { if (*p == '\0') mtree_err("missing number"); numbers[argc++] = strtoul(p, &ep, 0); if (*ep != '\0') mtree_err("invalid number `%s'", p); if (argc > MAX_PACK_ARGS) mtree_err("too many arguments"); } if (argc < 2) mtree_err("not enough arguments"); result = (*pack)(argc, numbers, &error); if (error != NULL) mtree_err("%s", error); } else { result = (dev_t)strtoul(arg, &ep, 0); if (*ep != '\0') mtree_err("invalid device `%s'", arg); } return (result); } static void replacenode(NODE *cur, NODE *new) { #define REPLACE(x) cur->x = new->x #define REPLACESTR(x) REPLACEPTR(cur->x,new->x) if (cur->type != new->type) { if (mtree_Mflag) { /* * merge entries with different types; we * don't want children retained in this case. */ REPLACE(type); free_nodes(cur->child); cur->child = NULL; } else { mtree_err( "existing entry for `%s', type `%s'" " does not match type `%s'", cur->name, nodetype(cur->type), nodetype(new->type)); } } REPLACE(st_size); REPLACE(st_mtimespec); REPLACESTR(slink); if (cur->slink != NULL) { if ((cur->slink = strdup(new->slink)) == NULL) mtree_err("memory allocation error"); if (strunvis(cur->slink, new->slink) == -1) mtree_err("strunvis failed on `%s'", new->slink); free(new->slink); } REPLACE(st_uid); REPLACE(st_gid); REPLACE(st_mode); REPLACE(st_rdev); REPLACE(st_flags); REPLACE(st_nlink); REPLACE(cksum); REPLACESTR(md5digest); REPLACESTR(rmd160digest); REPLACESTR(sha1digest); REPLACESTR(sha256digest); REPLACESTR(sha384digest); REPLACESTR(sha512digest); REPLACESTR(tags); REPLACE(lineno); REPLACE(flags); free(new); } static void set(char *t, NODE *ip) { int type, value, len; gid_t gid; uid_t uid; char *kw, *val, *md, *ep; void *m; while ((kw = strsep(&t, "= \t")) != NULL) { if (*kw == '\0') continue; if (strcmp(kw, "all") == 0) mtree_err("invalid keyword `all'"); ip->flags |= type = parsekey(kw, &value); if (!value) /* Just set flag bit (F_IGN and F_OPT) */ continue; while ((val = strsep(&t, " \t")) != NULL && *val == '\0') continue; if (val == NULL) mtree_err("missing value"); switch (type) { case F_CKSUM: ip->cksum = strtoul(val, &ep, 10); if (*ep) mtree_err("invalid checksum `%s'", val); break; case F_DEV: ip->st_rdev = parsedev(val); break; case F_FLAGS: #ifndef GNUPORT if (strcmp("none", val) == 0) ip->st_flags = 0; else if (string_to_flags(&val, &ip->st_flags, NULL) != 0) mtree_err("invalid flag `%s'", val); #endif break; case F_GID: ip->st_gid = (gid_t)strtoul(val, &ep, 10); if (*ep) mtree_err("invalid gid `%s'", val); break; case F_GNAME: if (mtree_Wflag) /* don't parse if whacking */ break; if (gid_from_group(val, &gid) == -1) mtree_err("unknown group `%s'", val); ip->st_gid = gid; break; case F_MD5: if (val[0]=='0' && val[1]=='x') md=&val[2]; else md=val; if ((ip->md5digest = strdup(md)) == NULL) mtree_err("memory allocation error"); break; case F_MODE: if ((m = setmode(val)) == NULL) mtree_err("cannot set file mode `%s' (%s)", val, strerror(errno)); ip->st_mode = getmode(m, 0); free(m); break; case F_NLINK: ip->st_nlink = (nlink_t)strtoul(val, &ep, 10); if (*ep) mtree_err("invalid link count `%s'", val); break; case F_RMD160: if (val[0]=='0' && val[1]=='x') md=&val[2]; else md=val; if ((ip->rmd160digest = strdup(md)) == NULL) mtree_err("memory allocation error"); break; case F_SHA1: if (val[0]=='0' && val[1]=='x') md=&val[2]; else md=val; if ((ip->sha1digest = strdup(md)) == NULL) mtree_err("memory allocation error"); break; case F_SIZE: ip->st_size = (off_t)strtoll(val, &ep, 10); if (*ep) mtree_err("invalid size `%s'", val); break; case F_SLINK: if ((ip->slink = strdup(val)) == NULL) mtree_err("memory allocation error"); if (strunvis(ip->slink, val) == -1) mtree_err("strunvis failed on `%s'", val); break; case F_TAGS: len = strlen(val) + 3; /* "," + str + ",\0" */ if ((ip->tags = malloc(len)) == NULL) mtree_err("memory allocation error"); snprintf(ip->tags, len, ",%s,", val); break; case F_TIME: ip->st_mtimespec.tv_sec = (time_t)strtoll(val, &ep, 10); if (*ep != '.') mtree_err("invalid time `%s'", val); val = ep + 1; ip->st_mtimespec.tv_nsec = strtol(val, &ep, 10); if (*ep) mtree_err("invalid time `%s'", val); break; case F_TYPE: ip->type = parsetype(val); break; case F_UID: ip->st_uid = (uid_t)strtoul(val, &ep, 10); if (*ep) mtree_err("invalid uid `%s'", val); break; case F_UNAME: if (mtree_Wflag) /* don't parse if whacking */ break; if (uid_from_user(val, &uid) == -1) mtree_err("unknown user `%s'", val); ip->st_uid = uid; break; case F_SHA256: if (val[0]=='0' && val[1]=='x') md=&val[2]; else md=val; if ((ip->sha256digest = strdup(md)) == NULL) mtree_err("memory allocation error"); break; case F_SHA384: if (val[0]=='0' && val[1]=='x') md=&val[2]; else md=val; if ((ip->sha384digest = strdup(md)) == NULL) mtree_err("memory allocation error"); break; case F_SHA512: if (val[0]=='0' && val[1]=='x') md=&val[2]; else md=val; if ((ip->sha512digest = strdup(md)) == NULL) mtree_err("memory allocation error"); break; default: mtree_err( "set(): unsupported key type 0x%x (INTERNAL ERROR)", type); /* NOTREACHED */ } } } static void unset(char *t, NODE *ip) { char *p; while ((p = strsep(&t, " \t")) != NULL) { if (*p == '\0') continue; ip->flags &= ~parsekey(p, NULL); } } /* * addchild -- * Add the centry node as a child of the pathparent node. If * centry is a duplicate, call replacenode(). If centry is not * a duplicate, insert it into the linked list referenced by * pathparent->child. Keep the list sorted if Sflag is set. */ static void addchild(NODE *pathparent, NODE *centry) { NODE *samename; /* node with the same name as centry */ NODE *replacepos; /* if non-NULL, centry should replace this node */ NODE *insertpos; /* if non-NULL, centry should be inserted * after this node */ NODE *cur; /* for stepping through the list */ NODE *last; /* the last node in the list */ int cmp; samename = NULL; replacepos = NULL; insertpos = NULL; last = NULL; cur = pathparent->child; if (cur == NULL) { /* centry is pathparent's first and only child node so far */ pathparent->child = centry; return; } /* * pathparent already has at least one other child, so add the * centry node to the list. * * We first scan through the list looking for an existing node * with the same name (setting samename), and also looking * for the correct position to replace or insert the new node * (setting replacepos and/or insertpos). */ for (; cur != NULL; last = cur, cur = cur->next) { if (strcmp(centry->name, cur->name) == 0) { samename = cur; } if (mtree_Sflag) { cmp = nodecmp(centry, cur); if (cmp == 0) { replacepos = cur; } else if (cmp > 0) { insertpos = cur; } } } if (! mtree_Sflag) { if (samename != NULL) { /* replace node with same name */ replacepos = samename; } else { /* add new node at end of list */ insertpos = last; } } if (samename != NULL) { /* * We found a node with the same name above. Call * replacenode(), which will either exit with an error, * or replace the information in the samename node and * free the information in the centry node. */ replacenode(samename, centry); if (samename == replacepos) { /* The just-replaced node was in the correct position */ return; } if (samename == insertpos || samename->prev == insertpos) { /* * We thought the new node should be just before * or just after the replaced node, but that would * be equivalent to just retaining the replaced node. */ return; } /* * The just-replaced node is in the wrong position in * the list. This can happen if sort order depends on * criteria other than the node name. * * Make centry point to the just-replaced node. Unlink * the just-replaced node from the list, and allow it to * be insterted in the correct position later. */ centry = samename; if (centry->prev) centry->prev->next = centry->next; else { /* centry->next is the new head of the list */ pathparent->child = centry->next; assert(centry->next != NULL); } if (centry->next) centry->next->prev = centry->prev; centry->prev = NULL; centry->next = NULL; } if (insertpos == NULL) { /* insert centry at the beginning of the list */ pathparent->child->prev = centry; centry->next = pathparent->child; centry->prev = NULL; pathparent->child = centry; } else { /* insert centry into the list just after insertpos */ centry->next = insertpos->next; insertpos->next = centry; centry->prev = insertpos; if (centry->next) centry->next->prev = centry; } return; } /* * nodecmp -- * used as a comparison function by addchild() to control the order * in which entries appear within a list of sibling nodes. We make * directories sort after non-directories, but otherwise sort in * strcmp() order. * * Keep this in sync with dcmp() in create.c. */ static int nodecmp(const NODE *a, const NODE *b) { if ((a->type & F_DIR) != 0) { if ((b->type & F_DIR) == 0) return 1; } else if ((b->type & F_DIR) != 0) return -1; return strcmp(a->name, b->name); } makefs/src/usr.sbin/makefs/walk.c010064400000000000000000000432371134456644100141760ustar00/* $NetBSD: walk.c,v 1.24 2008/12/28 21:51:46 christos Exp $ */ /* * Copyright (c) 2009, 2010 * Thorsten Glaser * Copyright (c) 2001 Wasabi Systems, Inc. * All rights reserved. * * Written by Luke Mewburn 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. */ #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #include #if defined(__RCSID) && !defined(__lint) __IDSTRING(mbsdid, "$MirOS: src/usr.sbin/makefs/walk.c,v 1.11 2010/03/07 00:02:17 tg Exp $"); __RCSID("$NetBSD: walk.c,v 1.24 2008/12/28 21:51:46 christos Exp $"); #endif /* !__lint */ #include #include #include #include #include #include #include #include #include #include #include #include "makefs.h" #include "mtree.h" static void apply_specdir(const char *, NODE *, fsnode *, int); static void apply_specentry(const char *, NODE *, fsnode *); static fsnode *create_fsnode(const char *, struct stat *); static fsinode *link_check(fsinode *); static uint32_t vinode = 3; /* * walk_dir -- * build a tree of fsnodes from `dir', with a parent fsnode of `parent' * (which may be NULL for the root of the tree). * each "level" is a directory, with the "." entry guaranteed to be * at the start of the list, and without ".." entries. */ fsnode * walk_dir(const char *dir, fsnode *parent) { fsnode *first, *cur, *prev; DIR *dirp; struct dirent *dent; char *path; struct stat stbuf; assert(dir != NULL); if ((path = malloc(maxpathlen + 1)) == NULL) err(1, "malloc"); if (debug & DEBUG_WALK_DIR) printf("walk_dir: %s %p\n", dir, parent); if ((dirp = opendir(dir)) == NULL) err(1, "Can't opendir `%s'", dir); first = prev = NULL; while ((dent = readdir(dirp)) != NULL) { if (strcmp(dent->d_name, "..") == 0) continue; if (debug & DEBUG_WALK_DIR_NODE) printf("scanning %s/%s\n", dir, dent->d_name); if ((size_t)snprintf(path, maxpathlen, "%s/%s", dir, dent->d_name) >= maxpathlen) errx(1, "Pathname too long."); if (lstat(path, &stbuf) == -1) err(1, "Can't lstat `%s'", path); #ifdef S_ISSOCK if (S_ISSOCK(stbuf.st_mode & S_IFMT)) { if (debug & DEBUG_WALK_DIR_NODE) printf(" skipping socket %s\n", path); continue; } #endif cur = create_fsnode(dent->d_name, &stbuf); cur->parent = parent; if (strcmp(dent->d_name, ".") == 0) { /* ensure "." is at the start of the list */ if (cur->parent) cur->inode->serno = cur->parent->inode->serno; cur->next = first; first = cur; if (! prev) prev = cur; } else { /* not "." */ if (prev) prev->next = cur; prev = cur; if (!first) first = cur; if (S_ISDIR(cur->type)) { cur->inode->serno = vinode++; cur->child = walk_dir(path, cur); continue; } } if (stbuf.st_nlink > 1) { fsinode *curino; curino = link_check(cur->inode); if (curino != NULL) { free(cur->inode); cur->inode = curino; cur->inode->nlink++; if (debug & DEBUG_WALK_DIR_LINKCHECK) printf("link_check: found [%llu, %llu]\n", (unsigned long long)curino->st.st_dev, (unsigned long long)curino->st.st_ino); } } if (!cur->inode->serno) cur->inode->serno = vinode++; if (S_ISLNK(cur->type)) { int llen; char *slink; if ((slink = malloc(stbuf.st_size + 1)) == NULL) err(1, "malloc"); llen = readlink(path, slink, stbuf.st_size); if (llen == -1) err(1, "Readlink `%s'", path); slink[llen] = '\0'; cur->symlink = slink; } } for (cur = first; cur != NULL; cur = cur->next) cur->first = first; if (closedir(dirp) == -1) err(1, "Can't closedir `%s'", dir); free(path); return (first); } static fsnode * create_fsnode(const char *name, struct stat *stbuf) { fsnode *cur; if ((cur = calloc(1, sizeof(fsnode))) == NULL || (cur->name = strdup(name)) == NULL || (cur->inode = calloc(1, sizeof(fsinode))) == NULL) err(1, "Memory allocation error"); cur->type = stbuf->st_mode & S_IFMT; cur->inode->nlink = 1; cur->inode->st = *stbuf; return (cur); } /* * free_fsnodes -- * Removes node from tree and frees it and all of * its decendents. */ void free_fsnodes(fsnode *node) { fsnode *cur, *next; assert(node != NULL); /* for ".", start with actual parent node */ if (node->first == node) { assert(node->name[0] == '.' && node->name[1] == '\0'); if (node->parent) { assert(node->parent->child == node); node = node->parent; } } /* Find ourselves in our sibling list and unlink */ if (node->first != node) { for (cur = node->first; cur->next; cur = cur->next) { if (cur->next == node) { cur->next = node->next; node->next = NULL; break; } } } for (cur = node; cur != NULL; cur = next) { next = cur->next; if (cur->child) { cur->child->parent = NULL; free_fsnodes(cur->child); } if (cur->inode->nlink-- == 1) free(cur->inode); if (cur->symlink) free(cur->symlink); free(cur->name); free(cur); } } /* * apply_specfile -- * read in the mtree(8) specfile, and apply it to the tree * at dir,parent. parameters in parent on equivalent types * will be changed to those found in specfile, and missing * entries will be added. */ void apply_specfile(const char *specfile, const char *dir, fsnode *parent, int speconly) { struct timeval start; FILE *fp; NODE *root; assert(specfile != NULL); assert(parent != NULL); if (debug & DEBUG_APPLY_SPECFILE) printf("apply_specfile: %s, %s %p\n", specfile, dir, parent); /* read in the specfile */ if ((fp = fopen(specfile, "r")) == NULL) err(1, "Can't open `%s'", specfile); TIMER_START(start); root = spec(fp); TIMER_RESULTS(start, "spec"); if (fclose(fp) == EOF) err(1, "Can't close `%s'", specfile); /* perform some sanity checks */ if (root == NULL) errx(1, "Specfile `%s' did not contain a tree", specfile); assert(strcmp(root->name, ".") == 0); assert(root->type == F_DIR); /* merge in the changes */ apply_specdir(dir, root, parent, speconly); free_nodes(root); } static void apply_specdir(const char *dir, NODE *specnode, fsnode *dirnode, int speconly) { char *path; NODE *curnode; fsnode *curfsnode; assert(specnode != NULL); assert(dirnode != NULL); if ((path = malloc(maxpathlen + 1)) == NULL) err(1, "malloc"); if (debug & DEBUG_APPLY_SPECFILE) printf("apply_specdir: %s %p %p\n", dir, specnode, dirnode); if (specnode->type != F_DIR) errx(1, "Specfile node `%s/%s' is not a directory", dir, specnode->name); if (dirnode->type != S_IFDIR) errx(1, "Directory node `%s/%s' is not a directory", dir, dirnode->name); apply_specentry(dir, specnode, dirnode); /* Remove any filesystem nodes not found in specfile */ /* XXX inefficient. This is O^2 in each dir and it would * have been better never to have walked this part of the tree * to begin with */ if (speconly) { fsnode *next; assert(dirnode->name[0] == '.' && dirnode->name[1] == '\0'); for (curfsnode = dirnode->next; curfsnode != NULL; curfsnode = next) { next = curfsnode->next; for (curnode = specnode->child; curnode != NULL; curnode = curnode->next) { if (strcmp(curnode->name, curfsnode->name) == 0) break; } if (curnode == NULL) { if (debug & DEBUG_APPLY_SPECONLY) { printf("apply_specdir: trimming %s/%s %p\n", dir, curfsnode->name, curfsnode); } free_fsnodes(curfsnode); } } } /* now walk specnode->child matching up with dirnode */ for (curnode = specnode->child; curnode != NULL; curnode = curnode->next) { if (debug & DEBUG_APPLY_SPECENTRY) printf("apply_specdir: spec %s\n", curnode->name); for (curfsnode = dirnode->next; curfsnode != NULL; curfsnode = curfsnode->next) { #if 0 /* too verbose for now */ if (debug & DEBUG_APPLY_SPECENTRY) printf("apply_specdir: dirent %s\n", curfsnode->name); #endif if (strcmp(curnode->name, curfsnode->name) == 0) break; } if ((size_t)snprintf(path, maxpathlen, "%s/%s", dir, curnode->name) >= maxpathlen) errx(1, "Pathname too long."); if (curfsnode == NULL) { /* need new entry */ struct stat stbuf; /* * don't add optional spec entries * that lack an existing fs entry */ if ((curnode->flags & F_OPT) && lstat(path, &stbuf) == -1) continue; /* check that enough info is provided */ #define NODETEST(t, m) \ if (!(t)) \ errx(1, "`%s': %s not provided", path, m) NODETEST(curnode->flags & F_TYPE, "type"); NODETEST(curnode->flags & F_MODE, "mode"); /* XXX: require F_TIME ? */ NODETEST(curnode->flags & F_GID || curnode->flags & F_GNAME, "group"); NODETEST(curnode->flags & F_UID || curnode->flags & F_UNAME, "user"); if (curnode->type == F_BLOCK || curnode->type == F_CHAR) NODETEST(curnode->flags & F_DEV, "device number"); #undef NODETEST if (debug & DEBUG_APPLY_SPECFILE) printf("apply_specdir: adding %s\n", curnode->name); /* build minimal fsnode */ memset(&stbuf, 0, sizeof(stbuf)); stbuf.st_mode = nodetoino(curnode->type); stbuf.st_nlink = 1; stbuf.st_mtime = stbuf.st_atime = stbuf.st_ctime = start_time.tv_sec; #if HAVE_STRUCT_STAT_ST_MTIMENSEC stbuf.st_mtimensec = stbuf.st_atimensec = stbuf.st_ctimensec = start_time.tv_nsec; #endif curfsnode = create_fsnode(curnode->name, &stbuf); curfsnode->parent = dirnode->parent; curfsnode->first = dirnode; curfsnode->next = dirnode->next; dirnode->next = curfsnode; if (curfsnode->type == S_IFDIR) { /* for dirs, make "." entry as well */ curfsnode->child = create_fsnode(".", &stbuf); curfsnode->child->parent = curfsnode; curfsnode->child->first = curfsnode->child; } if (curfsnode->type == S_IFLNK) { assert(curnode->slink != NULL); /* for symlinks, copy the target */ if ((curfsnode->symlink = strdup(curnode->slink)) == NULL) err(1, "Memory allocation error"); } } apply_specentry(dir, curnode, curfsnode); if (curnode->type == F_DIR) { if (curfsnode->type != S_IFDIR) errx(1, "`%s' is not a directory", path); assert (curfsnode->child != NULL); apply_specdir(path, curnode, curfsnode->child, speconly); } } free(path); } static void apply_specentry(const char *dir, NODE *specnode, fsnode *dirnode) { assert(specnode != NULL); assert(dirnode != NULL); if (nodetoino(specnode->type) != dirnode->type) errx(1, "`%s/%s' type mismatch: specfile %s, tree %s", dir, specnode->name, inode_type(nodetoino(specnode->type)), inode_type(dirnode->type)); if (debug & DEBUG_APPLY_SPECENTRY) printf("apply_specentry: %s/%s\n", dir, dirnode->name); #define ASEPRINT(t, b, o, n) \ if (debug & DEBUG_APPLY_SPECENTRY) \ printf("\t\t\tchanging %s from " b " to " b "\n", \ t, o, n) if (specnode->flags & (F_GID | F_GNAME)) { ASEPRINT("gid", "%d", dirnode->inode->st.st_gid, specnode->st_gid); dirnode->inode->st.st_gid = specnode->st_gid; } if (specnode->flags & F_MODE) { ASEPRINT("mode", "%#o", dirnode->inode->st.st_mode & ALLPERMS, specnode->st_mode); dirnode->inode->st.st_mode &= ~ALLPERMS; dirnode->inode->st.st_mode |= (specnode->st_mode & ALLPERMS); } /* XXX: ignoring F_NLINK for now */ if (specnode->flags & F_SIZE) { ASEPRINT("size", "%lld", (long long)dirnode->inode->st.st_size, (long long)specnode->st_size); dirnode->inode->st.st_size = specnode->st_size; } if (specnode->flags & F_SLINK) { assert(dirnode->symlink != NULL); assert(specnode->slink != NULL); ASEPRINT("symlink", "%s", dirnode->symlink, specnode->slink); free(dirnode->symlink); if ((dirnode->symlink = strdup(specnode->slink)) == NULL) err(1, "Memory allocation error"); } if (specnode->flags & F_TIME) { ASEPRINT("time", "%ld", (long)dirnode->inode->st.st_mtime, (long)specnode->st_mtimespec.tv_sec); dirnode->inode->st.st_mtime = specnode->st_mtimespec.tv_sec; dirnode->inode->st.st_atime = specnode->st_mtimespec.tv_sec; dirnode->inode->st.st_ctime = start_time.tv_sec; #if HAVE_STRUCT_STAT_ST_MTIMENSEC dirnode->inode->st.st_mtimensec = specnode->st_mtimespec.tv_nsec; dirnode->inode->st.st_atimensec = specnode->st_mtimespec.tv_nsec; dirnode->inode->st.st_ctimensec = start_time.tv_nsec; #endif } if (specnode->flags & (F_UID | F_UNAME)) { ASEPRINT("uid", "%d", dirnode->inode->st.st_uid, specnode->st_uid); dirnode->inode->st.st_uid = specnode->st_uid; } #if HAVE_STRUCT_STAT_ST_FLAGS if (specnode->flags & F_FLAGS) { ASEPRINT("flags", "%#lX", (unsigned long)dirnode->inode->st.st_flags, (unsigned long)specnode->st_flags); dirnode->inode->st.st_flags = specnode->st_flags; } #endif if (specnode->flags & F_DEV) { ASEPRINT("rdev", "%#llx", (unsigned long long)dirnode->inode->st.st_rdev, (unsigned long long)specnode->st_rdev); dirnode->inode->st.st_rdev = specnode->st_rdev; } #undef ASEPRINT dirnode->flags |= FSNODE_F_HASSPEC; } /* * dump_fsnodes -- * dump the fsnodes from `cur', based in the directory `dir' */ void dump_fsnodes(const char *dir, fsnode *root) { fsnode *cur; char *path; assert (dir != NULL); if ((path = malloc(maxpathlen + 1)) == NULL) err(1, "malloc"); printf("dump_fsnodes: %s(%d) %p\n", dir, root->inode->serno, root); for (cur = root; cur != NULL; cur = cur->next) { if ((size_t)snprintf(path, maxpathlen, "%s/%s", dir, cur->name) >= maxpathlen) errx(1, "Pathname too long."); if (debug & DEBUG_DUMP_FSNODES_VERBOSE) printf("cur=%8p parent=%8p first=%8p ", cur, cur->parent, cur->first); printf("%7s %5d: %s", inode_type(cur->type), cur->inode->serno, path); if (S_ISLNK(cur->type)) { assert(cur->symlink != NULL); printf(" -> %s", cur->symlink); } else { assert (cur->symlink == NULL); } if (cur->inode->nlink > 1) printf(", nlinks=%d", cur->inode->nlink); putchar('\n'); if (cur->child) { assert (cur->type == S_IFDIR); dump_fsnodes(path, cur->child); } } printf("dump_fsnodes: finished %s\n", dir); free(path); } /* * inode_type -- * for a given inode type `mode', return a descriptive string. * for most cases, uses inotype() from mtree/misc.c */ const char * inode_type(mode_t mode) { if (S_ISLNK(mode)) return ("symlink"); /* inotype() returns "link"... */ return (inotype(mode)); } /* * link_check -- * return pointer to fsinode matching `entry's st_ino & st_dev if it exists, * otherwise add `entry' to table and return NULL */ /* This was borrowed from du.c and tweaked to keep an fsnode * pointer instead. -- dbj@netbsd.org */ static fsinode * link_check(fsinode *entry) { static struct entry { fsinode *data; } *htable; static int htshift; /* log(allocated size) */ static int htmask; /* allocated size - 1 */ static int htused; /* 2*number of insertions */ int h, h2; uint64_t tmp; /* this constant is (1<<64)/((1+sqrt(5))/2) * aka (word size)/(golden ratio) */ const uint64_t HTCONST = 11400714819323198485ULL; const int HTBITS = 64; /* Never store zero in hashtable */ assert(entry); /* Extend hash table if necessary, keep load under 0.5 */ if (htused<<1 >= htmask) { struct entry *ohtable; if (!htable) htshift = 10; /* starting hashtable size */ else htshift++; /* exponential hashtable growth */ htmask = (1 << htshift) - 1; htused = 0; ohtable = htable; htable = calloc(htmask+1, sizeof(*htable)); if (!htable) err(1, "Memory allocation error"); /* populate newly allocated hashtable */ if (ohtable) { int i; for (i = 0; i <= htmask>>1; i++) if (ohtable[i].data) link_check(ohtable[i].data); free(ohtable); } } /* multiplicative hashing */ tmp = entry->st.st_dev; tmp <<= HTBITS>>1; tmp |= entry->st.st_ino; tmp *= HTCONST; h = tmp >> (HTBITS - htshift); h2 = 1 | ( tmp >> (HTBITS - (htshift<<1) - 1)); /* must be odd */ /* open address hashtable search with double hash probing */ while (htable[h].data) { if ((htable[h].data->st.st_ino == entry->st.st_ino) && (htable[h].data->st.st_dev == entry->st.st_dev)) { return htable[h].data; } h = (h + h2) & htmask; } /* Insert the current entry into hashtable */ htable[h].data = entry; htused++; return NULL; } makefs/src/usr.sbin/mtree/Makefile010064400000000000000000000004561022730055600143660ustar00# $MirOS: src/usr.sbin/mtree/Makefile,v 1.3 2005/04/13 20:30:16 tg Exp $ # $OpenBSD: Makefile,v 1.7 2004/05/02 17:55:53 millert Exp $ PROG= mtree #CPPFLAGS+=-DDEBUG MAN= mtree.8 SRCS= compare.c crc.c create.c misc.c mtree.c spec.c verify.c CPPFLAGS+= $(INCS) LDADD+= $(LIBS) .include makefs/src/usr.sbin/mtree/compare.c010064400000000000000000000246671022730075500145330ustar00/** $MirOS: src/usr.sbin/mtree/compare.c,v 1.3 2005/04/13 20:32:23 tg Exp $ */ /* $NetBSD: compare.c,v 1.11 1996/09/05 09:56:48 mycroft Exp $ */ /* $OpenBSD: compare.c,v 1.20 2004/11/21 19:36:04 otto Exp $ */ /*- * 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. */ #include #include #include #include #include #include #include #ifdef __INTERIX #include #endif #include #include #include #include #include "mtree.h" #include "extern.h" __SCCSID("@(#)compare.c 8.1 (Berkeley) 6/6/93"); __RCSID("$MirOS: src/usr.sbin/mtree/compare.c,v 1.3 2005/04/13 20:32:23 tg Exp $"); extern int lflag, tflag, uflag; static char *ftype(u_int); #define INDENTNAMELEN 8 #define LABEL \ if (!label++) { \ len = printf("%s: ", RP(p)); \ if (len > INDENTNAMELEN) { \ tab = "\t"; \ (void)printf("\n"); \ } else { \ tab = ""; \ (void)printf("%*s", INDENTNAMELEN - (int)len, ""); \ } \ } #define REPLACE_COMMA(x) \ do { \ char *l; \ for (l = x; *l; l++) { \ if (*l == ',') \ *l = ' '; \ } \ } while (0) \ int compare(char *name, NODE *s, FTSENT *p) { u_int32_t len, val; int fd, label; char *cp, *tab = ""; label = 0; switch(s->type) { case F_BLOCK: if (!S_ISBLK(p->fts_statp->st_mode)) goto typeerr; break; case F_CHAR: if (!S_ISCHR(p->fts_statp->st_mode)) goto typeerr; break; case F_DIR: if (!S_ISDIR(p->fts_statp->st_mode)) goto typeerr; break; case F_FIFO: if (!S_ISFIFO(p->fts_statp->st_mode)) goto typeerr; break; case F_FILE: if (!S_ISREG(p->fts_statp->st_mode)) goto typeerr; break; case F_LINK: if (!S_ISLNK(p->fts_statp->st_mode)) goto typeerr; break; case F_SOCK: if (!S_ISSOCK(p->fts_statp->st_mode)) { typeerr: LABEL; (void)printf("\ttype (%s, %s)\n", ftype(s->type), inotype(p->fts_statp->st_mode)); } break; } /* Set the uid/gid first, then set the mode. */ if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) { LABEL; (void)printf("%suser (%u, %u", tab, (unsigned)s->st_uid, (unsigned)p->fts_statp->st_uid); if (uflag) if (chown(p->fts_accpath, s->st_uid, -1)) (void)printf(", not modified: %s)\n", strerror(errno)); else (void)printf(", modified)\n"); else (void)printf(")\n"); tab = "\t"; } if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) { LABEL; (void)printf("%sgid (%u, %u", tab, (unsigned)s->st_gid, (unsigned)p->fts_statp->st_gid); if (uflag) if (chown(p->fts_accpath, -1, s->st_gid)) (void)printf(", not modified: %s)\n", strerror(errno)); else (void)printf(", modified)\n"); else (void)printf(")\n"); tab = "\t"; } if (s->flags & F_MODE && s->st_mode != (p->fts_statp->st_mode & MBITS)) { if (lflag) { mode_t tmode, mode; tmode = s->st_mode; mode = p->fts_statp->st_mode & MBITS; /* * if none of the suid/sgid/etc bits are set, * then if the mode is a subset of the target, * skip. */ if (!((tmode & ~(S_IRWXU|S_IRWXG|S_IRWXO)) || (mode & ~(S_IRWXU|S_IRWXG|S_IRWXO)))) if ((mode | tmode) == tmode) goto skip; } LABEL; (void)printf("%spermissions (%#o, %#lo", tab, (unsigned)s->st_mode, (unsigned long)p->fts_statp->st_mode & MBITS); if (uflag) if (chmod(p->fts_accpath, s->st_mode)) (void)printf(", not modified: %s)\n", strerror(errno)); else (void)printf(", modified)\n"); else (void)printf(")\n"); tab = "\t"; skip: ; } if (s->flags & F_NLINK && s->type != F_DIR && s->st_nlink != p->fts_statp->st_nlink) { LABEL; (void)printf("%slink count (%u, %u)\n", tab, (unsigned)s->st_nlink, (unsigned)p->fts_statp->st_nlink); tab = "\t"; } if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size) { LABEL; (void)printf("%ssize (%qd, %qd)\n", tab, (long long)s->st_size, (long long)p->fts_statp->st_size); tab = "\t"; } /* * XXX * Since utimes(2) only takes a timeval, there's no point in * comparing the low bits of the timespec nanosecond field. This * will only result in mismatches that we can never fix. * * Doesn't display microsecond differences. */ if (s->flags & F_TIME) { struct timeval tv[2]; #ifdef __INTERIX tv[1].tv_sec = s->st_mtimespec.tv_sec; tv[0].tv_usec = 0; tv[1].tv_sec = p->fts_statp->st_mtime; tv[1].tv_usec = 0; #else TIMESPEC_TO_TIMEVAL(&tv[0], &s->st_mtimespec); TIMESPEC_TO_TIMEVAL(&tv[1], &p->fts_statp->st_mtimespec); #endif if (tv[0].tv_sec != tv[1].tv_sec || tv[0].tv_usec != tv[1].tv_usec) { LABEL; (void)printf("%smodification time (%.24s, ", tab, ctime(&s->st_mtimespec.tv_sec)); (void)printf("%.24s", #ifdef __INTERIX ctime(&p->fts_statp->st_mtime)); #else ctime(&p->fts_statp->st_mtimespec.tv_sec)); #endif if (tflag) { #ifdef __INTERIX struct utimbuf u; u.actime = tv[0].tv_sec; u.modtime = tv[0].tv_sec; if (utime(p->fts_accpath, &u)) #else tv[1] = tv[0]; if (utimes(p->fts_accpath, tv)) #endif (void)printf(", not modified: %s)\n", strerror(errno)); else (void)printf(", modified)\n"); } else (void)printf(")\n"); tab = "\t"; } } if (s->flags & F_CKSUM) { if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) { LABEL; (void)printf("%scksum: %s: %s\n", tab, p->fts_accpath, strerror(errno)); tab = "\t"; } else if (crc(fd, &val, &len)) { (void)close(fd); LABEL; (void)printf("%scksum: %s: %s\n", tab, p->fts_accpath, strerror(errno)); tab = "\t"; } else { (void)close(fd); if (s->cksum != val) { LABEL; (void)printf("%scksum (%u, %u)\n", tab, s->cksum, val); } tab = "\t"; } } if (s->flags & F_MD5) { char *new_digest, buf[MD5_DIGEST_STRING_LENGTH]; new_digest = MD5File(p->fts_accpath, buf); if (!new_digest) { LABEL; printf("%sMD5File: %s: %s\n", tab, p->fts_accpath, strerror(errno)); tab = "\t"; } else if (strcmp(new_digest, s->md5digest)) { LABEL; printf("%sMD5 (%s, %s)\n", tab, s->md5digest, new_digest); tab = "\t"; } } if (s->flags & F_RMD160) { char *new_digest, buf[RMD160_DIGEST_STRING_LENGTH]; new_digest = RMD160File(p->fts_accpath, buf); if (!new_digest) { LABEL; printf("%sRMD160File: %s: %s\n", tab, p->fts_accpath, strerror(errno)); tab = "\t"; } else if (strcmp(new_digest, s->rmd160digest)) { LABEL; printf("%sRMD160 (%s, %s)\n", tab, s->rmd160digest, new_digest); tab = "\t"; } } if (s->flags & F_SHA1) { char *new_digest, buf[SHA1_DIGEST_STRING_LENGTH]; new_digest = SHA1File(p->fts_accpath, buf); if (!new_digest) { LABEL; printf("%sSHA1File: %s: %s\n", tab, p->fts_accpath, strerror(errno)); tab = "\t"; } else if (strcmp(new_digest, s->sha1digest)) { LABEL; printf("%sSHA1 (%s, %s)\n", tab, s->sha1digest, new_digest); tab = "\t"; } } if (s->flags & F_SLINK && strcmp(cp = rlink(name), s->slink)) { LABEL; (void)printf("%slink ref (%s, %s)\n", tab, cp, s->slink); } #ifndef __INTERIX if (s->flags & F_FLAGS && s->file_flags != p->fts_statp->st_flags) { char *db_flags = NULL; char *cur_flags = NULL; if ((db_flags = fflagstostr(s->file_flags)) == NULL || (cur_flags = fflagstostr(p->fts_statp->st_flags)) == NULL) { LABEL; (void)printf("%sflags: %s %s\n", tab, p->fts_accpath, strerror(errno)); tab = "\t"; if (db_flags != NULL) free(db_flags); if (cur_flags != NULL) free(cur_flags); } else { LABEL; REPLACE_COMMA(db_flags); REPLACE_COMMA(cur_flags); printf("%sflags (%s, %s", tab, (*db_flags == '\0') ? "-" : db_flags, (*cur_flags == '\0') ? "-" : cur_flags); tab = "\t"; if (uflag) if (chflags(p->fts_accpath, s->file_flags)) (void)printf(", not modified: %s)\n", strerror(errno)); else (void)printf(", modified)\n"); else (void)printf(")\n"); tab = "\t"; free(db_flags); free(cur_flags); } } #endif return (label); } char * inotype(u_int type) { switch(type & S_IFMT) { case S_IFBLK: return ("block"); case S_IFCHR: return ("char"); case S_IFDIR: return ("dir"); case S_IFIFO: return ("fifo"); case S_IFREG: return ("file"); case S_IFLNK: return ("link"); case S_IFSOCK: return ("socket"); default: return ("unknown"); } /* NOTREACHED */ } static char * ftype(u_int type) { switch(type) { case F_BLOCK: return ("block"); case F_CHAR: return ("char"); case F_DIR: return ("dir"); case F_FIFO: return ("fifo"); case F_FILE: return ("file"); case F_LINK: return ("link"); case F_SOCK: return ("socket"); default: return ("unknown"); } /* NOTREACHED */ } char * rlink(char *name) { static char lbuf[MAXPATHLEN]; int len; if ((len = readlink(name, lbuf, sizeof(lbuf)-1)) == -1) error("%s: %s", name, strerror(errno)); lbuf[len] = '\0'; return (lbuf); } makefs/src/usr.sbin/mtree/crc.c010064400000000000000000000142331020120134700136250ustar00/* $OpenBSD: crc.c,v 1.2 2004/08/01 18:32:20 deraadt Exp $ */ /* $NetBSD: crc.c,v 1.7 1996/02/27 21:29:53 jtc Exp $ */ /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * James W. Williams of NASA Goddard Space Flight Center. * * 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[] = "@(#)crc.c 8.1 (Berkeley) 6/17/93"; static char rcsid[] = "$NetBSD: crc.c,v 1.7 1996/02/27 21:29:53 jtc Exp $"; #else static char rcsid[] = "$OpenBSD: crc.c,v 1.2 2004/08/01 18:32:20 deraadt Exp $"; #endif #endif /* not lint */ #include #include #include "extern.h" static const u_int32_t crctab[] = { 0x0, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 }; /* * Compute a POSIX 1003.2 checksum. This routine has been broken out so that * other programs can use it. It takes a file descriptor to read from and * locations to store the crc and the number of bytes read. It returns 0 on * success and 1 on failure. Errno is set on failure. */ u_int32_t crc_total = ~0; /* The crc over a number of files. */ int crc(int fd, u_int32_t *cval, u_int32_t *clen) { u_char *p; int nr; u_int32_t crc, len; u_char buf[16 * 1024]; #define COMPUTE(var, ch) (var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)] crc = len = 0; crc_total = ~crc_total; while ((nr = read(fd, buf, sizeof(buf))) > 0) for (len += nr, p = buf; nr--; ++p) { COMPUTE(crc, *p); COMPUTE(crc_total, *p); } if (nr < 0) return (1); *clen = len; /* Include the length of the file. */ for (; len != 0; len >>= 8) { COMPUTE(crc, len & 0xff); COMPUTE(crc_total, len & 0xff); } *cval = ~crc; crc_total = ~crc_total; return (0); } makefs/src/usr.sbin/mtree/create.c010064400000000000000000000253251022730075600143410ustar00/** $MirOS: src/usr.sbin/mtree/create.c,v 1.3 2005/04/13 20:32:24 tg Exp $ */ /* $NetBSD: create.c,v 1.11 1996/09/05 09:24:19 mycroft Exp $ */ /* $OpenBSD: create.c,v 1.24 2004/11/21 19:36:04 otto Exp $ */ /*- * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mtree.h" #include "extern.h" __SCCSID("@(#)create.c 8.1 (Berkeley) 6/6/93"); __RCSID("$MirOS: src/usr.sbin/mtree/create.c,v 1.3 2005/04/13 20:32:24 tg Exp $"); #define INDENTNAMELEN 15 #define MAXLINELEN 80 extern u_int32_t crc_total; extern int ftsoptions; extern int dflag, iflag, nflag, sflag; extern u_int keys; extern char fullpath[MAXPATHLEN]; static gid_t gid; static uid_t uid; static mode_t mode; static void output(int, int *, const char *, ...); static int statd(FTS *, FTSENT *, uid_t *, gid_t *, mode_t *); static void statf(int, FTSENT *); void cwalk(void) { FTS *t; FTSENT *p; time_t clock; char *argv[2], host[MAXHOSTNAMELEN]; int indent = 0; (void)time(&clock); (void)gethostname(host, sizeof(host)); (void)printf( "#\t user: %s\n#\tmachine: %s\n#\t tree: %s\n#\t date: %s", getlogin(), host, fullpath, ctime(&clock)); argv[0] = "."; argv[1] = NULL; if ((t = fts_open(argv, ftsoptions, dsort)) == NULL) error("fts_open: %s", strerror(errno)); while ((p = fts_read(t))) { if (iflag) indent = p->fts_level * 4; switch(p->fts_info) { case FTS_D: if (!dflag) (void)printf("\n"); if (!nflag) (void)printf("# %s\n", p->fts_path); statd(t, p, &uid, &gid, &mode); statf(indent, p); break; case FTS_DP: if (!nflag && (p->fts_level > 0)) (void)printf("%*s# %s\n", indent, "", p->fts_path); (void)printf("%*s..\n", indent, ""); if (!dflag) (void)printf("\n"); break; case FTS_DNR: case FTS_ERR: case FTS_NS: (void)fprintf(stderr, "mtree: %s: %s\n", p->fts_path, strerror(p->fts_errno)); break; default: if (!dflag) statf(indent, p); break; } } (void)fts_close(t); if (sflag && keys & F_CKSUM) (void)fprintf(stderr, "mtree: %s checksum: %u\n", fullpath, crc_total); } static void statf(int indent, FTSENT *p) { struct group *gr; struct passwd *pw; u_int32_t len, val; int fd, offset; char *name, *escaped_name; size_t esc_len; esc_len = p->fts_namelen * 4 + 1; escaped_name = malloc(esc_len); if (escaped_name == NULL) error("statf: %s", strerror(errno)); strnvis(escaped_name, p->fts_name, esc_len, VIS_WHITE | VIS_OCTAL); if (iflag || S_ISDIR(p->fts_statp->st_mode)) offset = printf("%*s%s", indent, "", escaped_name); else offset = printf("%*s %s", indent, "", escaped_name); free(escaped_name); if (offset > (INDENTNAMELEN + indent)) offset = MAXLINELEN; else offset += printf("%*s", (INDENTNAMELEN + indent) - offset, ""); if (!S_ISREG(p->fts_statp->st_mode) && !dflag) output(indent, &offset, "type=%s", inotype(p->fts_statp->st_mode)); if (p->fts_statp->st_uid != uid) { if (keys & F_UNAME) { if ((pw = getpwuid(p->fts_statp->st_uid)) != NULL) { output(indent, &offset, "uname=%s", pw->pw_name); } else { error("could not get uname for uid=%u", p->fts_statp->st_uid); } } if (keys & F_UID) output(indent, &offset, "uid=%u", p->fts_statp->st_uid); } if (p->fts_statp->st_gid != gid) { if (keys & F_GNAME) { if ((gr = getgrgid(p->fts_statp->st_gid)) != NULL) { output(indent, &offset, "gname=%s", gr->gr_name); } else { error("could not get gname for gid=%u", p->fts_statp->st_gid); } } if (keys & F_GID) output(indent, &offset, "gid=%u", p->fts_statp->st_gid); } if (keys & F_MODE && (p->fts_statp->st_mode & MBITS) != mode) output(indent, &offset, "mode=%#o", p->fts_statp->st_mode & MBITS); if (keys & F_NLINK && p->fts_statp->st_nlink != 1) output(indent, &offset, "nlink=%u", p->fts_statp->st_nlink); if (keys & F_SIZE && S_ISREG(p->fts_statp->st_mode)) output(indent, &offset, "size=%qd", p->fts_statp->st_size); if (keys & F_TIME) #ifdef __INTERIX output(indent, &offset, "time=%ld.0", p->fts_statp->st_mtime); #else output(indent, &offset, "time=%ld.%ld", p->fts_statp->st_mtimespec.tv_sec, p->fts_statp->st_mtimespec.tv_nsec); #endif if (keys & F_CKSUM && S_ISREG(p->fts_statp->st_mode)) { if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0 || crc(fd, &val, &len)) error("%s: %s", p->fts_accpath, strerror(errno)); (void)close(fd); output(indent, &offset, "cksum=%lu", val); } if (keys & F_MD5 && S_ISREG(p->fts_statp->st_mode)) { char *md5digest, buf[MD5_DIGEST_STRING_LENGTH]; md5digest = MD5File(p->fts_accpath,buf); if (!md5digest) error("%s: %s", p->fts_accpath, strerror(errno)); else output(indent, &offset, "md5digest=%s", md5digest); } if (keys & F_RMD160 && S_ISREG(p->fts_statp->st_mode)) { char *rmd160digest, buf[RMD160_DIGEST_STRING_LENGTH]; rmd160digest = RMD160File(p->fts_accpath,buf); if (!rmd160digest) error("%s: %s", p->fts_accpath, strerror(errno)); else output(indent, &offset, "rmd160digest=%s", rmd160digest); } if (keys & F_SHA1 && S_ISREG(p->fts_statp->st_mode)) { char *sha1digest, buf[SHA1_DIGEST_STRING_LENGTH]; sha1digest = SHA1File(p->fts_accpath,buf); if (!sha1digest) error("%s: %s", p->fts_accpath, strerror(errno)); else output(indent, &offset, "sha1digest=%s", sha1digest); } if (keys & F_SLINK && (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) { name = rlink(p->fts_accpath); esc_len = strlen(name) * 4 + 1; escaped_name = malloc(esc_len); if (escaped_name == NULL) error("statf: %s", strerror(errno)); strnvis(escaped_name, name, esc_len, VIS_WHITE | VIS_OCTAL); output(indent, &offset, "link=%s", escaped_name); free(escaped_name); } #ifndef __INTERIX if (keys & F_FLAGS && !S_ISLNK(p->fts_statp->st_mode)) { char *file_flags; file_flags = fflagstostr(p->fts_statp->st_flags); if (file_flags == NULL) error("%s", strerror(errno)); if (*file_flags != '\0') output(indent, &offset, "flags=%s", file_flags); else output(indent, &offset, "flags=none"); free(file_flags); } #endif (void)putchar('\n'); } #define MAXGID 5000 #define MAXUID 5000 #define MAXMODE MBITS + 1 static int statd(FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode) { FTSENT *p; gid_t sgid; uid_t suid; mode_t smode; struct group *gr; struct passwd *pw; gid_t savegid = *pgid; uid_t saveuid = *puid; mode_t savemode = *pmode; int maxgid; int maxuid; u_short maxmode; gid_t g[MAXGID]; uid_t u[MAXUID]; mode_t m[MAXMODE]; static int first = 1; if ((p = fts_children(t, 0)) == NULL) { if (errno) error("%s: %s", RP(parent), strerror(errno)); return (1); } bzero(g, sizeof(g)); bzero(u, sizeof(u)); bzero(m, sizeof(m)); maxuid = maxgid = maxmode = 0; for (; p; p = p->fts_link) { if (!dflag || (dflag && S_ISDIR(p->fts_statp->st_mode))) { smode = p->fts_statp->st_mode & MBITS; if (smode < MAXMODE && ++m[smode] > maxmode) { savemode = smode; maxmode = m[smode]; } sgid = p->fts_statp->st_gid; if (sgid < MAXGID && ++g[sgid] > maxgid) { savegid = sgid; maxgid = g[sgid]; } suid = p->fts_statp->st_uid; if (suid < MAXUID && ++u[suid] > maxuid) { saveuid = suid; maxuid = u[suid]; } } } /* * If the /set record is the same as the last one we do not need to output * a new one. So first we check to see if anything changed. Note that we * always output a /set record for the first directory. */ if ((((keys & F_UNAME) | (keys & F_UID)) && (*puid != saveuid)) || (((keys & F_GNAME) | (keys & F_GID)) && (*pgid != savegid)) || ((keys & F_MODE) && (*pmode != savemode)) || (first)) { first = 0; if (dflag) (void)printf("/set type=dir"); else (void)printf("/set type=file"); if (keys & F_UNAME) { if ((pw = getpwuid(saveuid)) != NULL) (void)printf(" uname=%s", pw->pw_name); else error("could not get uname for uid=%u", saveuid); } if (keys & F_UID) (void)printf(" uid=%u", (unsigned)saveuid); if (keys & F_GNAME) { if ((gr = getgrgid(savegid)) != NULL) (void)printf(" gname=%s", gr->gr_name); else error("could not get gname for gid=%u", savegid); } if (keys & F_GID) (void)printf(" gid=%u", (unsigned)savegid); if (keys & F_MODE) (void)printf(" mode=%#o", (unsigned)savemode); if (keys & F_NLINK) (void)printf(" nlink=1"); (void)printf("\n"); *puid = saveuid; *pgid = savegid; *pmode = savemode; } return (0); } int dsort(const FTSENT **a, const FTSENT **b) { if (S_ISDIR((*a)->fts_statp->st_mode)) { if (!S_ISDIR((*b)->fts_statp->st_mode)) return (1); } else if (S_ISDIR((*b)->fts_statp->st_mode)) return (-1); return (strcmp((*a)->fts_name, (*b)->fts_name)); } void output(int indent, int *offset, const char *fmt, ...) { va_list ap; char buf[1024]; va_start(ap, fmt); (void)vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); if (*offset + strlen(buf) > MAXLINELEN - 3) { (void)printf(" \\\n%*s", INDENTNAMELEN + indent, ""); *offset = INDENTNAMELEN + indent; } *offset += printf(" %s", buf) + 1; } makefs/src/usr.sbin/mtree/extern.h010064400000000000000000000041221020120134700143640ustar00/* $OpenBSD: extern.h,v 1.7 2004/11/21 19:36:04 otto Exp $ */ /* $NetBSD: extern.h,v 1.3 1995/03/07 21:12:07 cgd Exp $ */ /*- * Copyright (c) 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. * * @(#)extern.h 8.1 (Berkeley) 6/6/93 */ struct _node; struct _ftsent; int compare(char *, struct _node *, struct _ftsent *); int dsort(const struct _ftsent **, const struct _ftsent **); int crc(int, u_int32_t *, u_int32_t *); void cwalk(void); void error(const char *, ...); char *inotype(u_int); u_int parsekey(char *, int *); char *rlink(char *); struct _node *spec(void); int verify(void); makefs/src/usr.sbin/mtree/misc.c010064400000000000000000000066251020120134700140170ustar00/* $OpenBSD: misc.c,v 1.18 2004/08/01 18:32:20 deraadt Exp $ */ /* $NetBSD: misc.c,v 1.4 1995/03/07 21:26:23 cgd Exp $ */ /*- * Copyright (c) 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. * * @(#)misc.c 8.1 (Berkeley) 6/6/93 */ #include #include #include #include #include "mtree.h" #include "extern.h" extern int lineno; typedef struct _key { char *name; /* key name */ u_int val; /* value */ #define NEEDVALUE 0x01 u_int flags; } KEY; /* NB: the following table must be sorted lexically. */ static KEY keylist[] = { {"cksum", F_CKSUM, NEEDVALUE}, {"flags", F_FLAGS, NEEDVALUE}, {"gid", F_GID, NEEDVALUE}, {"gname", F_GNAME, NEEDVALUE}, {"ignore", F_IGN, 0}, {"link", F_SLINK, NEEDVALUE}, {"md5digest", F_MD5, NEEDVALUE}, {"mode", F_MODE, NEEDVALUE}, {"nlink", F_NLINK, NEEDVALUE}, {"nochange", F_NOCHANGE, 0}, {"optional", F_OPT, 0}, {"rmd160digest",F_RMD160, NEEDVALUE}, {"sha1digest", F_SHA1, NEEDVALUE}, {"size", F_SIZE, NEEDVALUE}, {"time", F_TIME, NEEDVALUE}, {"type", F_TYPE, NEEDVALUE}, {"uid", F_UID, NEEDVALUE}, {"uname", F_UNAME, NEEDVALUE}, }; u_int parsekey(char *name, int *needvaluep) { KEY *k, tmp; int keycompare(const void *, const void *); tmp.name = name; k = (KEY *)bsearch(&tmp, keylist, sizeof(keylist) / sizeof(KEY), sizeof(KEY), keycompare); if (k == NULL) error("unknown keyword %s", name); if (needvaluep) *needvaluep = k->flags & NEEDVALUE ? 1 : 0; return (k->val); } int keycompare(const void *a, const void *b) { return (strcmp(((KEY *)a)->name, ((KEY *)b)->name)); } void error(const char *fmt, ...) { va_list ap; va_start(ap, fmt); (void)fflush(NULL); (void)fprintf(stderr, "\nmtree: "); (void)vfprintf(stderr, fmt, ap); va_end(ap); (void)fprintf(stderr, "\n"); if (lineno) (void)fprintf(stderr, "mtree: failed at line %d of the specification\n", lineno); exit(1); /* NOTREACHED */ } makefs/src/usr.sbin/mtree/mtree.8010064400000000000000000000342041341050352300141250ustar00.\" $MirOS: src/usr.sbin/mtree/mtree.8,v 1.5 2018/12/25 19:38:32 tg Exp $ .\" $OpenBSD: mtree.8,v 1.29 2004/12/30 10:01:47 jmc Exp $ .\" $NetBSD: mtree.8,v 1.4 1995/03/07 21:26:25 cgd Exp $ .\" .\" Copyright (c) 1989, 1990, 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. .\" .\" @(#)mtree.8 8.2 (Berkeley) 12/11/93 .\" .\"- .\" Copyright (c) 2008, 2009, 2010, 2016, 2018 .\" mirabilos .\"- .\" Try to make GNU groff and AT&T nroff more compatible .\" * ` generates ‘ in gnroff, so use \` .\" * ' generates ’ in gnroff, \' generates ´, so use \*(aq .\" * - generates ‐ in gnroff, \- generates −, so .tr it to - .\" thus use - for hyphens and \- for minus signs and option dashes .\" * ~ is size-reduced and placed atop in groff, so use \*(TI .\" * ^ is size-reduced and placed atop in groff, so use \*(ha .\" * \(en does not work in nroff, so use \*(en .\" * <>| are problematic, so redefine and use \*(Lt\*(Gt\*(Ba .\" Also make sure to use \& *before* a punctuation char that is to not .\" be interpreted as punctuation, and especially with two-letter words .\" but also (after) a period that does not end a sentence (“e.g.\&”). .\" The section after the "doc" macropackage has been loaded contains .\" additional code to convene between the UCB mdoc macropackage (and .\" its variant as BSD mdoc in groff) and the GNU mdoc macropackage. .\" .ie \n(.g \{\ . if \*[.T]ascii .tr \-\N'45' . if \*[.T]latin1 .tr \-\N'45' . if \*[.T]utf8 .tr \-\N'45' . ds <= \[<=] . ds >= \[>=] . ds Rq \[rq] . ds Lq \[lq] . ds sL \(aq . ds sR \(aq . if \*[.T]utf8 .ds sL ` . if \*[.T]ps .ds sL ` . if \*[.T]utf8 .ds sR ' . if \*[.T]ps .ds sR ' . ds aq \(aq . ds TI \(ti . ds ha \(ha . ds en \(en .\} .el \{\ . ds aq ' . ds TI ~ . ds ha ^ . ds en \(em .\} .\" .\" Implement .Dd with the Mdocdate RCS keyword .\" .rn Dd xD .de Dd .ie \\$1$Mdocdate: \{\ . xD \\$2 \\$3, \\$4 .\} .el .xD \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 .. .\" .\" .Dd must come before definition of .Mx, because when called .\" with -mandoc, it might implement .Mx itself, but we want to .\" use our own definition. And .Dd must come *first*, always. .\" .Dd $Mdocdate: December 25 2018 $ .\" .\" Check which macro package we use, and do other -mdoc setup. .\" .ie \n(.g \{\ . if \*[.T]utf8 .tr \[la]\*(Lt . if \*[.T]utf8 .tr \[ra]\*(Gt . ie d volume-ds-1 .ds tT gnu . el .ie d doc-volume-ds-1 .ds tT gnp . el .ds tT bsd .\} .el .ds tT ucb .\" .\" Implement .Mx (MirBSD) .\" .ie "\*(tT"gnu" \{\ . eo . de Mx . nr curr-font \n[.f] . nr curr-size \n[.ps] . ds str-Mx \f[\n[curr-font]]\s[\n[curr-size]u] . ds str-Mx1 \*[Tn-font-size]\%MirBSD\*[str-Mx] . if !\n[arg-limit] \ . if \n[.$] \{\ . ds macro-name Mx . parse-args \$@ . \} . if (\n[arg-limit] > \n[arg-ptr]) \{\ . nr arg-ptr +1 . ie (\n[type\n[arg-ptr]] == 2) \ . as str-Mx1 \~\*[arg\n[arg-ptr]] . el \ . nr arg-ptr -1 . \} . ds arg\n[arg-ptr] "\*[str-Mx1] . nr type\n[arg-ptr] 2 . ds space\n[arg-ptr] "\*[space] . nr num-args (\n[arg-limit] - \n[arg-ptr]) . nr arg-limit \n[arg-ptr] . if \n[num-args] \ . parse-space-vector . print-recursive .. . ec . ds sP \s0 . ds tN \*[Tn-font-size] .\} .el .ie "\*(tT"gnp" \{\ . eo . de Mx . nr doc-curr-font \n[.f] . nr doc-curr-size \n[.ps] . ds doc-str-Mx \f[\n[doc-curr-font]]\s[\n[doc-curr-size]u] . ds doc-str-Mx1 \*[doc-Tn-font-size]\%MirBSD\*[doc-str-Mx] . if !\n[doc-arg-limit] \ . if \n[.$] \{\ . ds doc-macro-name Mx . doc-parse-args \$@ . \} . if (\n[doc-arg-limit] > \n[doc-arg-ptr]) \{\ . nr doc-arg-ptr +1 . ie (\n[doc-type\n[doc-arg-ptr]] == 2) \ . as doc-str-Mx1 \~\*[doc-arg\n[doc-arg-ptr]] . el \ . nr doc-arg-ptr -1 . \} . ds doc-arg\n[doc-arg-ptr] "\*[doc-str-Mx1] . nr doc-type\n[doc-arg-ptr] 2 . ds doc-space\n[doc-arg-ptr] "\*[doc-space] . nr doc-num-args (\n[doc-arg-limit] - \n[doc-arg-ptr]) . nr doc-arg-limit \n[doc-arg-ptr] . if \n[doc-num-args] \ . doc-parse-space-vector . doc-print-recursive .. . ec . ds sP \s0 . ds tN \*[doc-Tn-font-size] .\} .el \{\ . de Mx . nr cF \\n(.f . nr cZ \\n(.s . ds aa \&\f\\n(cF\s\\n(cZ . if \\n(aC==0 \{\ . ie \\n(.$==0 \&MirBSD\\*(aa . el .aV \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 . \} . if \\n(aC>\\n(aP \{\ . nr aP \\n(aP+1 . ie \\n(C\\n(aP==2 \{\ . as b1 \&MirBSD\ #\&\\*(A\\n(aP\\*(aa . ie \\n(aC>\\n(aP \{\ . nr aP \\n(aP+1 . nR . \} . el .aZ . \} . el \{\ . as b1 \&MirBSD\\*(aa . nR . \} . \} .. .\} .\"- .Dt MTREE 8 .Os .Sh NAME .Nm mtree .Nd map a directory hierarchy .Sh SYNOPSIS .Nm mtree .Bk -words .Op Fl cdeilnqrtUux .Op Fl f Ar spec .Op Fl K Ar keywords .Op Fl k Ar keywords .Op Fl p Ar path .Op Fl s Ar seed .Ek .Sh DESCRIPTION The utility .Nm mtree compares the file hierarchy rooted in the current directory against a specification read from the standard input. Messages are written to the standard output for any files whose characteristics do not match the specification, or which are missing from either the file hierarchy or the specification. For an explanation of the directory hierarchy, see .Xr hier 7 . .Pp The options are as follows: .Bl -tag -width Ds .It Fl c Print a specification for the file hierarchy to the standard output. .It Fl d Ignore everything except directory type files. .It Fl e Don't complain about files that are in the file hierarchy, but not in the specification. .It Fl f Ar spec Read the specification from file .Ar spec , instead of from the standard input. .It Fl i Indents the output 4 spaces each time a directory level is descended when creating a specification with the .Fl c option. This does not affect either the /set statements or the comment before each directory. It does however affect the comment before the close of each directory. .It Fl K Ar keywords Add the specified (whitespace or comma separated) keywords to the current set of keywords. .It Fl k Ar keywords Use the .Dq type keyword plus the specified (whitespace or comma separated) keywords instead of the current set of keywords. .It Fl l Do .Dq loose permissions checks, in which more stringent permissions will match less stringent ones. For example, a file marked mode 0444 will pass a check for mode 0644. .Dq Loose checks apply only to read, write and execute permissions \*(en in particular, if other bits like the sticky bit or suid/sgid bits are set either in the specification or the file, exact checking will be performed. This flag may not be set at the same time as the .Fl u or .Fl U flags. .It Fl n Do not emit pathname comments when creating a specification. Normally a comment is emitted before each directory and before the close of that directory when using the .Fl c option. .It Fl p Ar path Use the file hierarchy rooted in .Ar path , instead of the current directory. .It Fl q Quiet mode. Do not complain when a .Dq missing directory cannot be created because it already exists. This occurs when the directory is a symbolic link. .It Fl r Remove any files in the file hierarchy that are not described in the specification. .It Fl s Ar seed Display a single checksum to the standard error output that represents all of the files for which the keyword .Cm cksum was specified. The checksum is seeded with the specified value. .It Fl t If a file's timestamp is different from the specification, .Dq touch it to match the specification (and list as modified). .It Fl U Modify the owner, group, and permissions of existing files to match the specification and create any missing directories. User, group, and permissions must all be specified for missing directories to be created. Exit with a status of 0 on success, 1 if any error occurred; a mismatch is not considered an error if it was corrected. .It Fl u Same as the .Fl U option except a status of 2 is returned if the file hierarchy did not match the specification. .It Fl x Don't descend below mount points in the file hierarchy. .El .Pp Specifications are mostly composed of .Dq keywords (i.e., strings that specify values relating to files). No keywords have default values, and if a keyword has no value set, no checks based on it are performed. .Pp Currently supported keywords are as follows: .Bl -tag -width Cm .It Cm cksum The checksum of the file using the default algorithm specified by the .Xr cksum 1 utility. .It Cm flags The current file's flags (whitespace or comma separated) in symbolic form as specified by .Xr chflags 1 . The string .Dq none may be used to indicate that no flags should be set on the file. .It Cm gid The file group as a numeric value. .It Cm gname The file group as a symbolic name. .It Cm ignore Ignore any file hierarchy below this file. .It Cm md5digest The MD5 message digest of the file. .It Cm mode The current file's permissions as a numeric (octal) or symbolic value. .It Cm nlink The number of hard links the file is expected to have. .It Cm nochange Do not change the attributes (owner, group, mode, etc) on a file or directory. .It Cm optional The file is optional; don't complain about the file if it's not in the file hierarchy. .It Cm rmd160digest The RIPEMD-160 message digest of the file. .It Cm sha1digest The SHA-1 message digest of the file. .It Cm uid The file owner as a numeric value. .It Cm uname The file owner as a symbolic name. .It Cm size The size, in bytes, of the file. .It Cm link The file the symbolic link is expected to reference. .It Cm time The last modification time of the file. .It Cm type The type of the file; may be set to any one of the following: .Pp .Bl -tag -width Cm -compact .It Cm block block special device .It Cm char character special device .It Cm dir directory .It Cm fifo FIFO .It Cm file regular file .It Cm link symbolic link .It Cm socket socket .El .El .Pp The default set of keywords are .Cm gid , .Cm mode , .Cm nlink , .Cm size , .Cm link , .Cm time , and .Cm uid . .Pp There are four types of lines in a specification. .Pp The first type of line sets a global value for a keyword, and consists of the string .Dq /set followed by whitespace, followed by sets of keyword/value pairs, separated by whitespace. Keyword/value pairs consist of a keyword, followed by an equals sign .Pq Sq = , followed by a value, without whitespace characters. Once a keyword has been set, its value remains unchanged until either reset or unset. .Pp The second type of line unsets keywords and consists of the string .Dq /unset , followed by whitespace, followed by one or more keywords, separated by whitespace. .Pp The third type of line is a file specification and consists of a file name, followed by whitespace, followed by zero or more whitespace separated keyword/value pairs. The file name may be preceded by whitespace characters. The file name may contain any of the standard file name matching characters .Po .Dq [ , .Dq \&] , .Dq \&? , or .Dq \&* .Pc , in which case files in the hierarchy will be associated with the first pattern that they match. .Pp Each of the keyword/value pairs consist of a keyword, followed by an equals sign, followed by the keyword's value, without whitespace characters. These values override, without changing, the global value of the corresponding keyword. .Pp All paths are relative. Specifying a directory will cause subsequent files to be searched for in that directory hierarchy. Which brings us to the last type of line in a specification: a line containing only the string .Dq \&.. causes the current directory path to ascend one level. .Pp Empty lines and lines whose first non-whitespace character is a hash mark .Pq Sq # are ignored. .Pp The .Nm mtree utility exits with a status of 0 on success, 1 if any error occurred, and 2 if the file hierarchy did not match the specification. A status of 2 is converted to a status of 0 if the .Fl U option is used. .Sh FILES .Bl -tag -width /etc/mtree -compact .It Pa /etc/mtree system specification directory .El .Sh EXAMPLES To detect system binaries that have been .Dq trojan horsed , it is recommended that .Nm mtree .Fl cK .Cm sha1digest be run on the filesystems, and a copy of the results stored on a different machine, or, at least, in encrypted form. The output file itself should be digested using the .Xr sha1 1 utility. Then, periodically, .Nm mtree and .Xr sha1 1 should be run against the on-line specifications. While it is possible for the bad guys to change the on-line specifications to conform to their modified binaries, it is believed to be impractical for them to create a modified specification which has the same SHA1 digest as the original. .Pp The .Fl d and .Fl u options can be used in combination to create directory hierarchies for distributions and other such things; the files in .Pa /etc/mtree were used to create almost all directories in a normal binary distribution. .Sh SEE ALSO .Xr chgrp 1 , .Xr chmod 1 , .Xr cksum 1 , .Xr md5 1 , .Xr rmd160 1 , .Xr sha1 1 , .Xr stat 2 , .Xr fts 3 , .Xr md5 3 , .Xr rmd160 3 , .Xr sha1 3 , .Xr hier 7 , .Xr chown 8 .Sh HISTORY The .Nm mtree utility appeared in .Bx 4.3 Reno . makefs/src/usr.sbin/mtree/mtree.c010064400000000000000000000104121020120134700141650ustar00/* $OpenBSD: mtree.c,v 1.18 2003/07/26 17:34:18 millert Exp $ */ /* $NetBSD: mtree.c,v 1.7 1996/09/05 23:29:22 thorpej Exp $ */ /*- * Copyright (c) 1989, 1990, 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) 1989, 1990, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static const char sccsid[] = "@(#)mtree.c 8.1 (Berkeley) 6/6/93"; #else static const char rcsid[] = "$OpenBSD: mtree.c,v 1.18 2003/07/26 17:34:18 millert Exp $"; #endif #endif /* not lint */ #include #include #include #include #include #include #include "mtree.h" #include "extern.h" extern u_int32_t crc_total; int ftsoptions = FTS_PHYSICAL; int cflag, dflag, eflag, iflag, lflag, nflag, qflag, rflag, sflag, tflag, uflag, Uflag; u_int keys; char fullpath[MAXPATHLEN]; static void usage(void); int main(int argc, char *argv[]) { extern int optind; extern char *optarg; int ch; char *dir, *p; int status; dir = NULL; keys = KEYDEFAULT; while ((ch = getopt(argc, argv, "cdef:iK:k:lnp:qrs:tUux")) != -1) switch((char)ch) { case 'c': cflag = 1; break; case 'd': dflag = 1; break; case 'e': eflag = 1; break; case 'f': if (!(freopen(optarg, "r", stdin))) error("%s: %s", optarg, strerror(errno)); break; case 'i': iflag = 1; break; case 'K': while ((p = strsep(&optarg, " \t,")) != NULL) if (*p != '\0') keys |= parsekey(p, NULL); break; case 'k': keys = F_TYPE; while ((p = strsep(&optarg, " \t,")) != NULL) if (*p != '\0') keys |= parsekey(p, NULL); break; case 'l': lflag = 1; break; case 'n': nflag = 1; break; case 'p': dir = optarg; break; case 'q': qflag = 1; break; case 'r': rflag = 1; break; case 's': sflag = 1; crc_total = ~strtol(optarg, &p, 0); if (*p) error("illegal seed value -- %s", optarg); break; case 't': tflag = 1; break; case 'U': Uflag = 1; uflag = 1; break; case 'u': uflag = 1; break; case 'x': ftsoptions |= FTS_XDEV; break; case '?': default: usage(); } argc -= optind; argv += optind; if (argc) usage(); if (dir && chdir(dir)) error("%s: %s", dir, strerror(errno)); if ((cflag || sflag) && !getcwd(fullpath, sizeof fullpath)) error("getcwd: %s", strerror(errno)); if (lflag == 1 && uflag == 1) error("-l and -u flags are mutually exclusive"); if (cflag) { cwalk(); exit(0); } status = verify(); if (Uflag && (status == MISMATCHEXIT)) status = 0; exit(status); } static void usage(void) { (void)fprintf(stderr, "usage: mtree [-cdeilnqrtUux] [-f spec] [-K key] [-k key] [-p path] [-s seed]\n"); exit(1); } makefs/src/usr.sbin/mtree/mtree.h010064400000000000000000000102651341415427400142170ustar00/** $MirOS: src/usr.sbin/mtree/mtree.h,v 1.3 2019/01/05 16:17:37 tg Exp $ */ /* $OpenBSD: mtree.h,v 1.10 2003/06/02 23:36:54 millert Exp $ */ /* $NetBSD: mtree.h,v 1.7 1995/03/07 21:26:27 cgd Exp $ */ /*- * Copyright (c) 1990, 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. * * @(#)mtree.h 8.1 (Berkeley) 6/6/93 */ #include #include #include #ifdef GNUPORT extern size_t strlcpy(char *, const char *, size_t); #endif #define KEYDEFAULT \ (F_GID | F_MODE | F_NLINK | F_SIZE | F_SLINK | F_TIME | F_UID) #define MISMATCHEXIT 2 typedef struct _node { struct _node *parent, *child; /* up, down */ struct _node *prev, *next; /* left, right */ off_t st_size; /* size */ struct timespec st_mtimespec; /* last modification time */ u_int32_t cksum; /* check sum */ char *md5digest; /* MD5 digest */ char *rmd160digest; /* RIPEMD-160 digest */ char *sha1digest; /* SHA-1 digest */ char *slink; /* symbolic link reference */ uid_t st_uid; /* uid */ gid_t st_gid; /* gid */ #ifdef S_ISTXT #define MBITS (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO) #else #define MBITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) #endif mode_t st_mode; /* mode */ nlink_t st_nlink; /* link count */ u_int32_t file_flags; /* file flags */ #define F_CKSUM 0x000001 /* checksum */ #define F_DONE 0x000002 /* directory done */ #define F_GID 0x000004 /* gid */ #define F_GNAME 0x000008 /* group name */ #define F_IGN 0x000010 /* ignore */ #define F_MAGIC 0x000020 /* name has magic chars */ #define F_MD5 0x000040 /* MD5 digest */ #define F_MODE 0x000080 /* mode */ #define F_NLINK 0x000100 /* number of links */ #define F_OPT 0x000200 /* existence optional */ #define F_RMD160 0x000400 /* RIPEMD-160 digest */ #define F_SHA1 0x000800 /* SHA-1 digest */ #define F_SIZE 0x001000 /* size */ #define F_SLINK 0x002000 /* link count */ #define F_TIME 0x004000 /* modification time */ #define F_TYPE 0x008000 /* file type */ #define F_UID 0x010000 /* uid */ #define F_UNAME 0x020000 /* user name */ #define F_VISIT 0x040000 /* file visited */ #define F_FLAGS 0x080000 /* file flags */ #define F_NOCHANGE 0x100000 /* do not change owner/mode */ u_int32_t flags; /* items set */ #define F_BLOCK 0x001 /* block special */ #define F_CHAR 0x002 /* char special */ #define F_DIR 0x004 /* directory */ #define F_FIFO 0x008 /* fifo */ #define F_FILE 0x010 /* regular file */ #define F_LINK 0x020 /* symbolic link */ #define F_SOCK 0x040 /* socket */ u_char type; /* file type */ char name[1]; /* file name (must be last) */ } NODE; #define RP(p) \ ((p)->fts_path[0] == '.' && (p)->fts_path[1] == '/' ? \ (p)->fts_path + 2 : (p)->fts_path) makefs/src/usr.sbin/mtree/spec.c010064400000000000000000000175631341415477000140410ustar00/** $MirOS: src/usr.sbin/mtree/spec.c,v 1.3 2019/01/05 16:22:53 tg Exp $ */ /* $NetBSD: spec.c,v 1.6 1995/03/07 21:12:12 cgd Exp $ */ /* $OpenBSD: spec.c,v 1.22 2004/08/01 18:32:20 deraadt Exp $ */ /*- * 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[] = "@(#)spec.c 8.1 (Berkeley) 6/6/93"; #else static const char rcsid[] = "$OpenBSD: spec.c,v 1.22 2004/08/01 18:32:20 deraadt Exp $"; #endif #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include "mtree.h" #include "extern.h" #ifdef GNUPORT extern mode_t getmode(const void *, mode_t); extern void *setmode(const char *); #endif int lineno; /* Current spec line number. */ static void set(char *, NODE *); static void unset(char *, NODE *); NODE * spec(void) { NODE *centry, *last; char *p; NODE ginfo, *root; int c_cur, c_next; char buf[2048]; size_t len; centry = last = root = NULL; bzero(&ginfo, sizeof(ginfo)); c_cur = c_next = 0; for (lineno = 1; fgets(buf, sizeof(buf), stdin); ++lineno, c_cur = c_next, c_next = 0) { /* Skip empty lines. */ if (buf[0] == '\n') continue; /* Find end of line. */ if ((p = strchr(buf, '\n')) == NULL) error("line %d too long", lineno); /* See if next line is continuation line. */ if (p[-1] == '\\') { --p; c_next = 1; } /* Null-terminate the line. */ *p = '\0'; /* Skip leading whitespace. */ for (p = buf; *p && isspace(*p); ++p); /* If nothing but whitespace or comment char, continue. */ if (!*p || *p == '#') continue; #ifdef DEBUG (void)fprintf(stderr, "line %d: {%s}\n", lineno, p); #endif if (c_cur) { set(p, centry); continue; } /* Grab file name, "$", "set", or "unset". */ if ((p = strtok(p, "\n\t ")) == NULL) error("missing field"); if (p[0] == '/') switch(p[1]) { case 's': if (strcmp(p + 1, "set")) break; set(NULL, &ginfo); continue; case 'u': if (strcmp(p + 1, "unset")) break; unset(NULL, &ginfo); continue; } if (strchr(p, '/')) error("slash character in file name"); if (!strcmp(p, "..")) { /* Don't go up, if haven't gone down. */ if (!root) goto noparent; if (last->type != F_DIR || last->flags & F_DONE) { if (last == root) goto noparent; last = last->parent; } last->flags |= F_DONE; continue; noparent: error("no parent node"); } len = strlen(p) + 1; /* NUL in struct _node */ if ((centry = calloc(1, sizeof(NODE) + len - 1)) == NULL) error("%s", strerror(errno)); *centry = ginfo; #define MAGIC "?*[" if (strpbrk(p, MAGIC)) centry->flags |= F_MAGIC; if (strunvis(centry->name, p) == -1) { fprintf(stderr, "mtree: filename (%s) encoded incorrectly\n", p); strlcpy(centry->name, p, len); } set(NULL, centry); if (!root) { last = root = centry; root->parent = root; } else if (last->type == F_DIR && !(last->flags & F_DONE)) { centry->parent = last; last = last->child = centry; } else { centry->parent = last->parent; centry->prev = last; last = last->next = centry; } } return (root); } static void set(char *t, NODE *ip) { int type; char *kw, *val = NULL; struct group *gr; struct passwd *pw; void *m; int value; u_int32_t fset, fclr; char *ep; size_t len; for (; (kw = strtok(t, "= \t\n")); t = NULL) { ip->flags |= type = parsekey(kw, &value); if (value && (val = strtok(NULL, " \t\n")) == NULL) error("missing value"); switch(type) { case F_CKSUM: ip->cksum = strtoul(val, &ep, 10); if (*ep) error("invalid checksum %s", val); break; case F_MD5: ip->md5digest = strdup(val); if (!ip->md5digest) error("%s", strerror(errno)); break; #ifndef __INTERIX case F_FLAGS: if (!strcmp(val, "none")) { ip->file_flags = 0; break; } if (strtofflags(&val, &fset, &fclr)) error("%s", strerror(errno)); ip->file_flags = fset; break; #endif case F_GID: ip->st_gid = strtoul(val, &ep, 10); if (*ep) error("invalid gid %s", val); break; case F_GNAME: if ((gr = getgrnam(val)) == NULL) error("unknown group %s", val); ip->st_gid = gr->gr_gid; break; case F_IGN: /* just set flag bit */ break; case F_MODE: if ((m = setmode(val)) == NULL) error("invalid file mode %s", val); ip->st_mode = getmode(m, 0); free(m); break; case F_NLINK: ip->st_nlink = strtoul(val, &ep, 10); if (*ep) error("invalid link count %s", val); break; case F_RMD160: ip->rmd160digest = strdup(val); if (!ip->rmd160digest) error("%s", strerror(errno)); break; case F_SHA1: ip->sha1digest = strdup(val); if (!ip->sha1digest) error("%s", strerror(errno)); break; case F_SIZE: ip->st_size = strtouq(val, &ep, 10); if (*ep) error("invalid size %s", val); break; case F_SLINK: len = strlen(val) + 1; if ((ip->slink = malloc(len)) == NULL) error("%s", strerror(errno)); if (strunvis(ip->slink, val) == -1) { fprintf(stderr, "mtree: filename (%s) encoded incorrectly\n", val); strlcpy(ip->slink, val, len); } break; case F_TIME: ip->st_mtimespec.tv_sec = strtoul(val, &ep, 10); if (*ep != '.') error("invalid time %s", val); val = ep + 1; ip->st_mtimespec.tv_nsec = strtoul(val, &ep, 10); if (*ep) error("invalid time %s", val); break; case F_TYPE: switch(*val) { case 'b': if (!strcmp(val, "block")) ip->type = F_BLOCK; break; case 'c': if (!strcmp(val, "char")) ip->type = F_CHAR; break; case 'd': if (!strcmp(val, "dir")) ip->type = F_DIR; break; case 'f': if (!strcmp(val, "file")) ip->type = F_FILE; if (!strcmp(val, "fifo")) ip->type = F_FIFO; break; case 'l': if (!strcmp(val, "link")) ip->type = F_LINK; break; case 's': if (!strcmp(val, "socket")) ip->type = F_SOCK; break; default: error("unknown file type %s", val); } break; case F_UID: ip->st_uid = strtoul(val, &ep, 10); if (*ep) error("invalid uid %s", val); break; case F_UNAME: if ((pw = getpwnam(val)) == NULL) error("unknown user %s", val); ip->st_uid = pw->pw_uid; break; } } } static void unset(char *t, NODE *ip) { char *p; while ((p = strtok(t, "\n\t "))) ip->flags &= ~parsekey(p, NULL); } makefs/src/usr.sbin/mtree/verify.c010064400000000000000000000133041020120134700143600ustar00/* $OpenBSD: verify.c,v 1.16 2004/11/21 19:36:04 otto Exp $ */ /* $NetBSD: verify.c,v 1.10 1995/03/07 21:26:28 cgd Exp $ */ /*- * Copyright (c) 1990, 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[] = "@(#)verify.c 8.1 (Berkeley) 6/6/93"; #else static const char rcsid[] = "$OpenBSD: verify.c,v 1.16 2004/11/21 19:36:04 otto Exp $"; #endif #endif /* not lint */ #include #include #include #include #include #include #include #include #include "mtree.h" #include "extern.h" extern int32_t crc_total; extern int ftsoptions; extern int dflag, eflag, qflag, rflag, sflag, uflag; extern char fullpath[MAXPATHLEN]; static NODE *root; static char path[MAXPATHLEN]; static void miss(NODE *, char *, size_t); static int vwalk(void); int verify(void) { int rval; root = spec(); rval = vwalk(); miss(root, path, sizeof(path)); return (rval); } static int vwalk(void) { FTS *t; FTSENT *p; NODE *ep, *level; int specdepth, rval; char *argv[2]; argv[0] = "."; argv[1] = NULL; if ((t = fts_open(argv, ftsoptions, dsort)) == NULL) error("fts_open: %s", strerror(errno)); level = root; specdepth = rval = 0; while ((p = fts_read(t))) { switch(p->fts_info) { case FTS_D: break; case FTS_DP: if (specdepth > p->fts_level) { for (level = level->parent; level->prev; level = level->prev); --specdepth; } continue; case FTS_DNR: case FTS_ERR: case FTS_NS: (void)fprintf(stderr, "mtree: %s: %s\n", RP(p), strerror(p->fts_errno)); continue; default: if (dflag) continue; } if (specdepth != p->fts_level) goto extra; for (ep = level; ep; ep = ep->next) if ((ep->flags & F_MAGIC && !fnmatch(ep->name, p->fts_name, FNM_PATHNAME)) || !strcmp(ep->name, p->fts_name)) { ep->flags |= F_VISIT; if ((ep->flags & F_NOCHANGE) == 0 && compare(ep->name, ep, p)) rval = MISMATCHEXIT; if (ep->flags & F_IGN) (void)fts_set(t, p, FTS_SKIP); else if (ep->child && ep->type == F_DIR && p->fts_info == FTS_D) { level = ep->child; ++specdepth; } break; } if (ep) continue; extra: if (!eflag) { (void)printf("extra: %s", RP(p)); if (rflag) { if ((S_ISDIR(p->fts_statp->st_mode) ? rmdir : unlink)(p->fts_accpath)) { (void)printf(", not removed: %s", strerror(errno)); } else (void)printf(", removed"); } (void)putchar('\n'); } (void)fts_set(t, p, FTS_SKIP); } (void)fts_close(t); if (sflag) (void)fprintf(stderr, "mtree: %s checksum: %u\n", fullpath, crc_total); return (rval); } static void miss(NODE *p, char *tail, size_t len) { int create; char *tp; for (; p; p = p->next) { if ((p->flags & F_OPT) && !(p->flags & F_VISIT)) continue; if (p->type != F_DIR && (dflag || p->flags & F_VISIT)) continue; (void)strlcpy(tail, p->name, len); if (!(p->flags & F_VISIT)) { /* Don't print missing message if file exists as a symbolic link and the -q flag is set. */ struct stat statbuf; if (qflag && stat(path, &statbuf) == 0) p->flags |= F_VISIT; else (void)printf("missing: %s", path); } if (p->type != F_DIR) { putchar('\n'); continue; } create = 0; if (!(p->flags & F_VISIT) && uflag) { if (!(p->flags & (F_UID | F_UNAME))) (void)printf(" (not created: user not specified)"); else if (!(p->flags & (F_GID | F_GNAME))) (void)printf(" (not created: group not specified)"); else if (!(p->flags & F_MODE)) (void)printf(" (not created: mode not specified)"); else if (mkdir(path, S_IRWXU)) (void)printf(" (not created: %s)", strerror(errno)); else { create = 1; (void)printf(" (created)"); } } if (!(p->flags & F_VISIT)) (void)putchar('\n'); for (tp = tail; *tp; ++tp); *tp = '/'; miss(p->child, tp + 1, len - (tp + 1 - tail)); *tp = '\0'; if (!create) continue; if (chown(path, p->st_uid, p->st_gid)) { (void)printf("%s: user/group/mode not modified: %s\n", path, strerror(errno)); continue; } if (chmod(path, p->st_mode)) (void)printf("%s: permissions not set: %s\n", path, strerror(errno)); } }