diff --git a/lib/libufs/libufs.h b/lib/libufs/libufs.h index 5045117b5d7d..cb8774454b38 100644 --- a/lib/libufs/libufs.h +++ b/lib/libufs/libufs.h @@ -1,171 +1,172 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2002 Juli Mallett. All rights reserved. * * This software was written by Juli Mallett for the * FreeBSD project. Redistribution and use in source and binary forms, with * or without modification, are permitted provided that the following * conditions are met: * * 1. Redistribution of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistribution 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. * * $FreeBSD$ */ #ifndef __LIBUFS_H__ #define __LIBUFS_H__ /* * libufs structures. */ union dinodep { struct ufs1_dinode *dp1; struct ufs2_dinode *dp2; }; /* * userland ufs disk. */ struct uufsd { const char *d_name; /* disk name */ int d_ufs; /* decimal UFS version */ int d_fd; /* raw device file descriptor */ long d_bsize; /* device bsize */ ufs2_daddr_t d_sblock; /* superblock location */ struct fs_summary_info *d_si; /* Superblock summary info */ caddr_t d_inoblock; /* inode block */ uint32_t d_inomin; /* low ino, not ino_t for ABI compat */ uint32_t d_inomax; /* high ino, not ino_t for ABI compat */ union dinodep d_dp; /* pointer to currently active inode */ union { struct fs d_fs; /* filesystem information */ char d_sb[MAXBSIZE]; /* superblock as buffer */ } d_sbunion; union { struct cg d_cg; /* cylinder group */ char d_buf[MAXBSIZE]; /* cylinder group storage */ } d_cgunion; int d_ccg; /* current cylinder group */ int d_lcg; /* last cylinder group (in d_cg) */ const char *d_error; /* human readable disk error */ - off_t d_sblockloc; /* where to look for the superblock */ + off_t d_sblockloc; /* where to look for the superblock */ + int d_lookupflags; /* flags to superblock lookup */ int d_mine; /* internal flags */ #define d_fs d_sbunion.d_fs #define d_sb d_sbunion.d_sb #define d_cg d_cgunion.d_cg }; /* * libufs macros (internal, non-exported). */ #ifdef _LIBUFS /* * Trace steps through libufs, to be used at entry and erroneous return. */ static inline void ERROR(struct uufsd *u, const char *str) { #ifdef _LIBUFS_DEBUGGING if (str != NULL) { fprintf(stderr, "libufs: %s", str); if (errno != 0) fprintf(stderr, ": %s", strerror(errno)); fprintf(stderr, "\n"); } #endif if (u != NULL) u->d_error = str; } #endif /* _LIBUFS */ __BEGIN_DECLS /* * libufs prototypes. */ /* * ffs_subr.c */ void ffs_clrblock(struct fs *, u_char *, ufs1_daddr_t); void ffs_clusteracct(struct fs *, struct cg *, ufs1_daddr_t, int); void ffs_fragacct(struct fs *, int, int32_t [], int); int ffs_isblock(struct fs *, u_char *, ufs1_daddr_t); int ffs_isfreeblock(struct fs *, u_char *, ufs1_daddr_t); void ffs_setblock(struct fs *, u_char *, ufs1_daddr_t); -int ffs_sbget(void *, struct fs **, off_t, char *, +int ffs_sbget(void *, struct fs **, off_t, int, char *, int (*)(void *, off_t, void **, int)); int ffs_sbput(void *, struct fs *, off_t, int (*)(void *, off_t, void *, int)); void ffs_update_dinode_ckhash(struct fs *, struct ufs2_dinode *); int ffs_verify_dinode_ckhash(struct fs *, struct ufs2_dinode *); /* * block.c */ ssize_t bread(struct uufsd *, ufs2_daddr_t, void *, size_t); ssize_t bwrite(struct uufsd *, ufs2_daddr_t, const void *, size_t); int berase(struct uufsd *, ufs2_daddr_t, ufs2_daddr_t); /* * cgroup.c */ ufs2_daddr_t cgballoc(struct uufsd *); int cgbfree(struct uufsd *, ufs2_daddr_t, long); ino_t cgialloc(struct uufsd *); int cgget(int, struct fs *, int, struct cg *); int cgput(int, struct fs *, struct cg *); int cgread(struct uufsd *); int cgread1(struct uufsd *, int); int cgwrite(struct uufsd *); int cgwrite1(struct uufsd *, int); /* * inode.c */ int getinode(struct uufsd *, union dinodep *, ino_t); int putinode(struct uufsd *); /* * sblock.c */ int sbread(struct uufsd *); int sbwrite(struct uufsd *, int); /* low level superblock read/write functions */ -int sbget(int, struct fs **, off_t); +int sbget(int, struct fs **, off_t, int); int sbput(int, struct fs *, int); /* * type.c */ int ufs_disk_close(struct uufsd *); int ufs_disk_fillout(struct uufsd *, const char *); int ufs_disk_fillout_blank(struct uufsd *, const char *); int ufs_disk_write(struct uufsd *); /* * crc32c.c */ uint32_t calculate_crc32c(uint32_t, const void *, size_t); __END_DECLS #endif /* __LIBUFS_H__ */ diff --git a/lib/libufs/sblock.c b/lib/libufs/sblock.c index 9ba012400f1d..5708d50aa4f9 100644 --- a/lib/libufs/sblock.c +++ b/lib/libufs/sblock.c @@ -1,227 +1,249 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2002 Juli Mallett. All rights reserved. * * This software was written by Juli Mallett for the * FreeBSD project. Redistribution and use in source and binary forms, with * or without modification, are permitted provided that the following * conditions are met: * * 1. Redistribution of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistribution 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int handle_disk_read(struct uufsd *, struct fs *, int); /* * Read the standard superblock. + * + * The following option flags can be or'ed into disk->d_lookupflags: + * + * UFS_NOMSG indicates that superblock inconsistency error messages + * should not be printed. + * + * UFS_NOCSUM causes only the superblock itself to be returned, but does + * not read in any auxillary data structures like the cylinder group + * summary information. */ int sbread(struct uufsd *disk) { struct fs *fs; int error; - error = sbget(disk->d_fd, &fs, disk->d_sblockloc); + error = sbget(disk->d_fd, &fs, disk->d_sblockloc, disk->d_lookupflags); return (handle_disk_read(disk, fs, error)); } static int handle_disk_read(struct uufsd *disk, struct fs *fs, int error) { ERROR(disk, NULL); if (error != 0) { switch (error) { case EIO: ERROR(disk, "non-existent or truncated superblock"); break; case ENOENT: ERROR(disk, "no usable known superblock found"); break; case EINTEGRITY: ERROR(disk, "superblock check-hash failure"); break; case ENOSPC: ERROR(disk, "failed to allocate space for superblock " "information"); break; case EINVAL: ERROR(disk, "The previous newfs operation on this " "volume did not complete.\nYou must complete " "newfs before using this volume."); break; default: ERROR(disk, "unknown superblock read error"); errno = EIO; break; } disk->d_ufs = 0; return (-1); } memcpy(&disk->d_fs, fs, fs->fs_sbsize); free(fs); fs = &disk->d_fs; if (fs->fs_magic == FS_UFS1_MAGIC) disk->d_ufs = 1; if (fs->fs_magic == FS_UFS2_MAGIC) disk->d_ufs = 2; disk->d_bsize = fs->fs_fsize / fsbtodb(fs, 1); disk->d_sblock = fs->fs_sblockloc / disk->d_bsize; disk->d_si = fs->fs_si; return (0); } int sbwrite(struct uufsd *disk, int all) { struct fs *fs; int rv; ERROR(disk, NULL); rv = ufs_disk_write(disk); if (rv == -1) { ERROR(disk, "failed to open disk for writing"); return (-1); } fs = &disk->d_fs; if ((errno = sbput(disk->d_fd, fs, all ? fs->fs_ncg : 0)) != 0) { switch (errno) { case EIO: ERROR(disk, "failed to write superblock"); break; default: ERROR(disk, "unknown superblock write error"); errno = EIO; break; } return (-1); } return (0); } /* * These are the low-level functions that actually read and write * the superblock and its associated data. The actual work is done by * the functions ffs_sbget and ffs_sbput in /sys/ufs/ffs/ffs_subr.c. */ static int use_pread(void *devfd, off_t loc, void **bufp, int size); static int use_pwrite(void *devfd, off_t loc, void *buf, int size); /* + * The following two functions read a superblock. Their flags + * parameter are made up of the following or'ed together options: + * + * UFS_NOMSG indicates that superblock inconsistency error messages + * should not be printed. + * + * UFS_NOCSUM causes only the superblock itself to be returned, but does + * not read in any auxillary data structures like the cylinder group + * summary information. + * * Read a superblock from the devfd device allocating memory returned - * in fsp. Also read the superblock summary information. + * in fsp. */ int -sbget(int devfd, struct fs **fsp, off_t sblockloc) +sbget(int devfd, struct fs **fsp, off_t sblockloc, int flags) { - return (ffs_sbget(&devfd, fsp, sblockloc, "user", use_pread)); + return (ffs_sbget(&devfd, fsp, sblockloc, flags, "user", use_pread)); } /* * A read function for use by user-level programs using libufs. */ static int use_pread(void *devfd, off_t loc, void **bufp, int size) { int fd; fd = *(int *)devfd; if ((*bufp = malloc(size)) == NULL) return (ENOSPC); if (pread(fd, *bufp, size, loc) != size) return (EIO); return (0); } /* * Write a superblock to the devfd device from the memory pointed to by fs. * Also write out the superblock summary information but do not free the * summary information memory. * * Additionally write out numaltwrite of the alternate superblocks. Use * fs->fs_ncg to write out all of the alternate superblocks. */ int sbput(int devfd, struct fs *fs, int numaltwrite) { struct csum *savedcsp; off_t savedactualloc; int i, error; if ((error = ffs_sbput(&devfd, fs, fs->fs_sblockactualloc, use_pwrite)) != 0) return (error); if (numaltwrite == 0) return (0); savedactualloc = fs->fs_sblockactualloc; - savedcsp = fs->fs_csp; - fs->fs_csp = NULL; + if (fs->fs_si != NULL) { + savedcsp = fs->fs_csp; + fs->fs_csp = NULL; + } for (i = 0; i < numaltwrite; i++) { fs->fs_sblockactualloc = dbtob(fsbtodb(fs, cgsblock(fs, i))); if ((error = ffs_sbput(&devfd, fs, fs->fs_sblockactualloc, use_pwrite)) != 0) { fs->fs_sblockactualloc = savedactualloc; fs->fs_csp = savedcsp; return (error); } } fs->fs_sblockactualloc = savedactualloc; - fs->fs_csp = savedcsp; + if (fs->fs_si != NULL) + fs->fs_csp = savedcsp; return (0); } /* * A write function for use by user-level programs using sbput in libufs. */ static int use_pwrite(void *devfd, off_t loc, void *buf, int size) { int fd; fd = *(int *)devfd; if (pwrite(fd, buf, size, loc) != size) return (EIO); return (0); } diff --git a/lib/libufs/sbread.3 b/lib/libufs/sbread.3 index e97469f86873..f579fc7ffdf1 100644 --- a/lib/libufs/sbread.3 +++ b/lib/libufs/sbread.3 @@ -1,148 +1,168 @@ .\" Author: Juli Mallett .\" Date: June 04, 2003 .\" Description: .\" Manual page for libufs functions: .\" sbget(3) .\" sbput(3) .\" sbread(3) .\" sbwrite(3) .\" .\" This file is in the public domain. .\" .\" $FreeBSD$ .\" .Dd September 2, 2020 .Dt SBREAD 3 .Os .Sh NAME .Nm sbget , sbput , sbread , sbwrite .Nd read and write superblocks of a UFS file system .Sh LIBRARY .Lb libufs .Sh SYNOPSIS .In sys/param.h .In sys/mount.h .In ufs/ufs/ufsmount.h .In ufs/ufs/dinode.h .In ufs/ffs/fs.h .In libufs.h .Ft int -.Fn sbget "int devfd" "struct fs **fsp" "off_t sblockloc" +.Fn sbget "int devfd" "struct fs **fsp" "off_t sblockloc" "int flags" .Ft int .Fn sbput "int devfd" "struct fs *fs" "int numaltwrite" .Ft int .Fn sbread "struct uufsd *disk" .Ft int .Fn sbwrite "struct uufsd *disk" "int all" .Sh DESCRIPTION The .Fn sbget and .Fn sbread functions provide superblock reads for .Xr libufs 3 consumers. The .Fn sbput and .Fn sbwrite functions provide superblock writes for .Xr libufs 3 consumers. .Pp The .Fn sbget function first allocates a buffer to hold the superblock. Using the .Va devfd file descriptor that references the filesystem disk, .Fn sbget reads the superblock located at the byte offset specified by .Va sblockloc into the allocated buffer. -If successful, it returns a pointer to the buffer containing the superblock in +The value +.Cm UFS_STDSB +may be specified for +.Va sblockloc +to request that the standard location for the superblock be read. +Flags are specified by +.Em or Ns 'ing +the following values: +.Pp +.Bl -tag -width UFS_NOHASHFAIL +.It Cm UFS_NOHASHFAIL +Will note if the check hash is wrong but will still return the superblock. +.It Cm UFS_NOMSG +Indicates that superblock inconsistency error messages should not be printed. +.It Cm UFS_NOCSUM +Causes only the superblock itself to be returned, but does not read in any auxiliary data structures like the cylinder group summary information. +.El +.Pp +If successful, +.Fn sbget +returns a pointer to the buffer containing the superblock in .Va fsp . The .Fn sbget function is safe to use in threaded applications. .Pp The .Fn sbput function writes the superblock specified by .Va fs to the location from which it was read on the disk referenced by the .Va devfd file descriptor. Additionally, the .Fn sbput function will update the first .Va numaltwrite alternate superblock locations. To update all the alternate superblocks, specify a .Va numaltwrite value of .Va fs->fs_ncg . The .Fn sbput function is safe to use in threaded applications. Note that the .Fn sbput function needs to be called only if the superblock has been modified and the on-disk copy needs to be updated. .Pp The .Fn sbread function reads the standard filesystem superblock into the .Va d_sb , structure embedded in the given user-land UFS disk structure. .Pp The .Fn sbwrite function writes the superblock from the .Va d_sb , structure embedded in the given user-land UFS disk structure to the location from which it was read. Additionally, the .Fn sbwrite function will write to all the alternate superblock locations if the .Fa all value is non-zero. .Sh RETURN VALUES .Rv -std sbread sbwrite The .Fn sbget and .Fn sbput functions return the value 0 if successful; otherwise they return one of the errors described below. .Sh ERRORS The errors returned by .Fn sbget and .Fn sbread include any of the errors specified for the library function .Xr bread 3 . Additionally, they may follow the .Xr libufs 3 error methodologies in situations where no usable superblock could be found. .Pp The errors returned by .Fn sbput and .Fn sbwrite include any of the errors specified for the library function .Xr bwrite 3 . .Sh SEE ALSO .Xr bread 3 , .Xr bwrite 3 , .Xr libufs 3 .Sh HISTORY These functions first appeared as part of .Xr libufs 3 in .Fx 5.0 . .Sh AUTHORS .An Juli Mallett Aq Mt jmallett@FreeBSD.org .An Marshall Kirk McKusick Aq Mt mckusick@FreeBSD.org diff --git a/lib/libufs/type.c b/lib/libufs/type.c index ac5b8d17e84c..80b26cdb9c84 100644 --- a/lib/libufs/type.c +++ b/lib/libufs/type.c @@ -1,207 +1,208 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2002 Juli Mallett. All rights reserved. * * This software was written by Juli Mallett for the * FreeBSD project. Redistribution and use in source and binary forms, with * or without modification, are permitted provided that the following * conditions are met: * * 1. Redistribution of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistribution 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Internally, track the 'name' value, it's ours. */ #define MINE_NAME 0x01 /* Track if its fd points to a writable device. */ #define MINE_WRITE 0x02 int ufs_disk_close(struct uufsd *disk) { ERROR(disk, NULL); close(disk->d_fd); disk->d_fd = -1; if (disk->d_inoblock != NULL) { free(disk->d_inoblock); disk->d_inoblock = NULL; } if (disk->d_mine & MINE_NAME) { free((char *)(uintptr_t)disk->d_name); disk->d_name = NULL; } if (disk->d_si != NULL) { free(disk->d_si->si_csp); free(disk->d_si); disk->d_si = NULL; } return (0); } int ufs_disk_fillout(struct uufsd *disk, const char *name) { if (ufs_disk_fillout_blank(disk, name) == -1) { return (-1); } if (sbread(disk) == -1) { ERROR(disk, "could not read superblock to fill out disk"); ufs_disk_close(disk); return (-1); } return (0); } int ufs_disk_fillout_blank(struct uufsd *disk, const char *name) { struct stat st; struct fstab *fs; struct statfs sfs; const char *oname; char dev[MAXPATHLEN]; int fd, ret; ERROR(disk, NULL); oname = name; again: if ((ret = stat(name, &st)) < 0) { if (*name != '/') { snprintf(dev, sizeof(dev), "%s%s", _PATH_DEV, name); name = dev; goto again; } /* * The given object doesn't exist, but don't panic just yet - * it may be still mount point listed in /etc/fstab, but without * existing corresponding directory. */ name = oname; } if (ret >= 0 && S_ISREG(st.st_mode)) { /* Possibly a disk image, give it a try. */ ; } else if (ret >= 0 && S_ISCHR(st.st_mode)) { /* This is what we need, do nothing. */ ; } else if ((fs = getfsfile(name)) != NULL) { /* * The given mount point is listed in /etc/fstab. * It is possible that someone unmounted file system by hand * and different file system is mounted on this mount point, * but we still prefer /etc/fstab entry, because on the other * hand, there could be /etc/fstab entry for this mount * point, but file system is not mounted yet (eg. noauto) and * statfs(2) will point us at different file system. */ name = fs->fs_spec; } else if (ret >= 0 && S_ISDIR(st.st_mode)) { /* * The mount point is not listed in /etc/fstab, so it may be * file system mounted by hand. */ if (statfs(name, &sfs) < 0) { ERROR(disk, "could not find special device"); return (-1); } strlcpy(dev, sfs.f_mntfromname, sizeof(dev)); name = dev; } else { ERROR(disk, "could not find special device"); return (-1); } fd = open(name, O_RDONLY); if (fd == -1) { ERROR(disk, "could not open special device"); return (-1); } disk->d_bsize = 1; disk->d_ccg = 0; disk->d_fd = fd; disk->d_inoblock = NULL; disk->d_inomin = 0; disk->d_inomax = 0; disk->d_lcg = 0; disk->d_mine = 0; disk->d_ufs = 0; disk->d_error = NULL; disk->d_si = NULL; - disk->d_sblockloc = STDSB; + disk->d_sblockloc = UFS_STDSB; + disk->d_lookupflags = 0; if (oname != name) { name = strdup(name); if (name == NULL) { ERROR(disk, "could not allocate memory for disk name"); return (-1); } disk->d_mine |= MINE_NAME; } disk->d_name = name; return (0); } int ufs_disk_write(struct uufsd *disk) { int fd; ERROR(disk, NULL); if (disk->d_mine & MINE_WRITE) return (0); fd = open(disk->d_name, O_RDWR); if (fd < 0) { ERROR(disk, "failed to open disk for writing"); return (-1); } close(disk->d_fd); disk->d_fd = fd; disk->d_mine |= MINE_WRITE; return (0); } diff --git a/sbin/dump/main.c b/sbin/dump/main.c index 8752f2c1bea5..779db5fb4b43 100644 --- a/sbin/dump/main.c +++ b/sbin/dump/main.c @@ -1,797 +1,797 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1980, 1991, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/1/95"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dump.h" #include "pathnames.h" int mapsize; /* size of the state maps */ char *usedinomap; /* map of allocated inodes */ char *dumpdirmap; /* map of directories to be dumped */ char *dumpinomap; /* map of files to be dumped */ char *disk; /* name of the disk file */ char *tape; /* name of the tape file */ char *popenout; /* popen(3) per-"tape" command */ int level; /* dump level of this dump */ int uflag; /* update flag */ int diskfd; /* disk file descriptor */ int pipeout; /* true => output to standard output */ int density = 0; /* density in bytes/0.1" " <- this is for hilit19 */ long tapesize; /* estimated tape size, blocks */ long tsize; /* tape size in 0.1" units */ int etapes; /* estimated number of tapes */ int nonodump; /* if set, do not honor UF_NODUMP user flags */ int unlimited; /* if set, write to end of medium */ int cachesize = 0; /* block cache size (in bytes), defaults to 0 */ int rsync_friendly; /* be friendly with rsync */ int notify = 0; /* notify operator flag */ int blockswritten = 0; /* number of blocks written on current tape */ int tapeno = 0; /* current tape number */ int ntrec = NTREC; /* # tape blocks in each tape record */ long blocksperfile; /* number of blocks per output file */ int cartridge = 0; /* Assume non-cartridge tape */ char *host = NULL; /* remote host (if any) */ time_t tstart_writing; /* when started writing the first tape block */ time_t tend_writing; /* after writing the last tape block */ int passno; /* current dump pass number */ struct fs *sblock; /* the file system super block */ long dev_bsize = 1; /* recalculated below */ int dev_bshift; /* log2(dev_bsize) */ int tp_bshift; /* log2(TP_BSIZE) */ int snapdump = 0; /* dumping live filesystem, so use snapshot */ static char *getmntpt(char *, int *); static long numarg(const char *, long, long); static void obsolete(int *, char **[]); static void usage(void) __dead2; int main(int argc, char *argv[]) { struct stat sb; ino_t ino; int dirty; union dinode *dp; struct fstab *dt; char *map, *mntpt; int ch, mode, mntflags; int i, ret, anydirskipped, bflag = 0, Tflag = 0, honorlevel = 1; int just_estimate = 0; ino_t maxino; char *tmsg; spcl.c_date = _time_to_time64(time(NULL)); tsize = 0; /* Default later, based on 'c' option for cart tapes */ dumpdates = _PATH_DUMPDATES; popenout = NULL; tape = NULL; if (TP_BSIZE / DEV_BSIZE == 0 || TP_BSIZE % DEV_BSIZE != 0) quit("TP_BSIZE must be a multiple of DEV_BSIZE\n"); level = 0; rsync_friendly = 0; if (argc < 2) usage(); obsolete(&argc, &argv); while ((ch = getopt(argc, argv, "0123456789aB:b:C:cD:d:f:h:LnP:RrSs:T:uWw")) != -1) switch (ch) { /* dump level */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': level = 10 * level + ch - '0'; break; case 'a': /* `auto-size', Write to EOM. */ unlimited = 1; break; case 'B': /* blocks per output file */ blocksperfile = numarg("number of blocks per file", 1L, 0L); break; case 'b': /* blocks per tape write */ ntrec = numarg("number of blocks per write", 1L, 1000L); break; case 'C': cachesize = numarg("cachesize", 0, 0) * 1024 * 1024; break; case 'c': /* Tape is cart. not 9-track */ cartridge = 1; break; case 'D': dumpdates = optarg; break; case 'd': /* density, in bits per inch */ density = numarg("density", 10L, 327670L) / 10; if (density >= 625 && !bflag) ntrec = HIGHDENSITYTREC; break; case 'f': /* output file */ if (popenout != NULL) errx(X_STARTUP, "You cannot use the P and f " "flags together.\n"); tape = optarg; break; case 'h': honorlevel = numarg("honor level", 0L, 10L); break; case 'L': snapdump = 1; break; case 'n': /* notify operators */ notify = 1; break; case 'P': if (tape != NULL) errx(X_STARTUP, "You cannot use the P and f " "flags together.\n"); popenout = optarg; break; case 'r': /* store slightly less data to be friendly to rsync */ if (rsync_friendly < 1) rsync_friendly = 1; break; case 'R': /* store even less data to be friendlier to rsync */ if (rsync_friendly < 2) rsync_friendly = 2; break; case 'S': /* exit after estimating # of tapes */ just_estimate = 1; break; case 's': /* tape size, feet */ tsize = numarg("tape size", 1L, 0L) * 12 * 10; break; case 'T': /* time of last dump */ spcl.c_ddate = unctime(optarg); if (spcl.c_ddate < 0) { (void)fprintf(stderr, "bad time \"%s\"\n", optarg); exit(X_STARTUP); } Tflag = 1; lastlevel = -1; break; case 'u': /* update /etc/dumpdates */ uflag = 1; break; case 'W': /* what to do */ case 'w': lastdump(ch); exit(X_FINOK); /* do nothing else */ default: usage(); } argc -= optind; argv += optind; if (argc < 1) { (void)fprintf(stderr, "Must specify disk or file system\n"); exit(X_STARTUP); } disk = *argv++; argc--; if (argc >= 1) { (void)fprintf(stderr, "Unknown arguments to dump:"); while (argc--) (void)fprintf(stderr, " %s", *argv++); (void)fprintf(stderr, "\n"); exit(X_STARTUP); } if (rsync_friendly && (level > 0)) { (void)fprintf(stderr, "%s %s\n", "rsync friendly options", "can be used only with level 0 dumps."); exit(X_STARTUP); } if (Tflag && uflag) { (void)fprintf(stderr, "You cannot use the T and u flags together.\n"); exit(X_STARTUP); } if (popenout) { tape = "child pipeline process"; } else if (tape == NULL && (tape = getenv("TAPE")) == NULL) tape = _PATH_DEFTAPE; if (strcmp(tape, "-") == 0) { pipeout++; tape = "standard output"; } if (blocksperfile) blocksperfile = rounddown(blocksperfile, ntrec); else if (!unlimited) { /* * Determine how to default tape size and density * * density tape size * 9-track 1600 bpi (160 bytes/.1") 2300 ft. * 9-track 6250 bpi (625 bytes/.1") 2300 ft. * cartridge 8000 bpi (100 bytes/.1") 1700 ft. * (450*4 - slop) * hilit19 hits again: " */ if (density == 0) density = cartridge ? 100 : 160; if (tsize == 0) tsize = cartridge ? 1700L*120L : 2300L*120L; } if (strchr(tape, ':')) { host = tape; tape = strchr(host, ':'); *tape++ = '\0'; #ifdef RDUMP if (strchr(tape, '\n')) { (void)fprintf(stderr, "invalid characters in tape\n"); exit(X_STARTUP); } if (rmthost(host) == 0) exit(X_STARTUP); #else (void)fprintf(stderr, "remote dump not enabled\n"); exit(X_STARTUP); #endif } (void)setuid(getuid()); /* rmthost() is the only reason to be setuid */ if (signal(SIGHUP, SIG_IGN) != SIG_IGN) signal(SIGHUP, sig); if (signal(SIGTRAP, SIG_IGN) != SIG_IGN) signal(SIGTRAP, sig); if (signal(SIGFPE, SIG_IGN) != SIG_IGN) signal(SIGFPE, sig); if (signal(SIGBUS, SIG_IGN) != SIG_IGN) signal(SIGBUS, sig); if (signal(SIGSEGV, SIG_IGN) != SIG_IGN) signal(SIGSEGV, sig); if (signal(SIGTERM, SIG_IGN) != SIG_IGN) signal(SIGTERM, sig); if (signal(SIGINT, interrupt) == SIG_IGN) signal(SIGINT, SIG_IGN); dump_getfstab(); /* /etc/fstab snarfed */ /* * disk can be either the full special file name, * the suffix of the special file name, * the special name missing the leading '/', * the file system name with or without the leading '/'. */ dt = fstabsearch(disk); if (dt != NULL) { disk = rawname(dt->fs_spec); if (disk == NULL) errx(X_STARTUP, "%s: unknown file system", dt->fs_spec); (void)strncpy(spcl.c_dev, dt->fs_spec, NAMELEN); (void)strncpy(spcl.c_filesys, dt->fs_file, NAMELEN); } else { (void)strncpy(spcl.c_dev, disk, NAMELEN); (void)strncpy(spcl.c_filesys, "an unlisted file system", NAMELEN); } spcl.c_dev[NAMELEN-1]='\0'; spcl.c_filesys[NAMELEN-1]='\0'; if ((mntpt = getmntpt(disk, &mntflags)) != NULL) { if (mntflags & MNT_RDONLY) { if (snapdump != 0) { msg("WARNING: %s\n", "-L ignored for read-only filesystem."); snapdump = 0; } } else if (snapdump == 0) { msg("WARNING: %s\n", "should use -L when dumping live read-write " "filesystems!"); } else { char snapname[BUFSIZ], snapcmd[BUFSIZ]; snprintf(snapname, sizeof snapname, "%s/.snap", mntpt); if ((stat(snapname, &sb) < 0) || !S_ISDIR(sb.st_mode)) { msg("WARNING: %s %s\n", "-L requested but snapshot location", snapname); msg(" %s: %s\n", "is not a directory", "dump downgraded, -L ignored"); snapdump = 0; } else { snprintf(snapname, sizeof snapname, "%s/.snap/dump_snapshot", mntpt); snprintf(snapcmd, sizeof snapcmd, "%s %s %s", _PATH_MKSNAP_FFS, mntpt, snapname); unlink(snapname); if (system(snapcmd) != 0) errx(X_STARTUP, "Cannot create %s: %s\n", snapname, strerror(errno)); if ((diskfd = open(snapname, O_RDONLY)) < 0) { unlink(snapname); errx(X_STARTUP, "Cannot open %s: %s\n", snapname, strerror(errno)); } unlink(snapname); if (fstat(diskfd, &sb) != 0) err(X_STARTUP, "%s: stat", snapname); spcl.c_date = _time_to_time64(sb.st_mtime); } } } else if (snapdump != 0) { msg("WARNING: Cannot use -L on an unmounted filesystem.\n"); snapdump = 0; } if (snapdump == 0) { if ((diskfd = open(disk, O_RDONLY)) < 0) err(X_STARTUP, "Cannot open %s", disk); if (fstat(diskfd, &sb) != 0) err(X_STARTUP, "%s: stat", disk); if (S_ISDIR(sb.st_mode)) errx(X_STARTUP, "%s: unknown file system", disk); } (void)strcpy(spcl.c_label, "none"); (void)gethostname(spcl.c_host, NAMELEN); spcl.c_level = level; spcl.c_type = TS_TAPE; if (rsync_friendly) { /* don't store real dump times */ spcl.c_date = 0; spcl.c_ddate = 0; } if (spcl.c_date == 0) { tmsg = "the epoch\n"; } else { time_t t = _time64_to_time(spcl.c_date); tmsg = ctime(&t); } msg("Date of this level %d dump: %s", level, tmsg); if (!Tflag && (!rsync_friendly)) getdumptime(); /* /etc/dumpdates snarfed */ if (spcl.c_ddate == 0) { tmsg = "the epoch\n"; } else { time_t t = _time64_to_time(spcl.c_ddate); tmsg = ctime(&t); } if (lastlevel < 0) msg("Date of last (level unknown) dump: %s", tmsg); else msg("Date of last level %d dump: %s", lastlevel, tmsg); msg("Dumping %s%s ", snapdump ? "snapshot of ": "", disk); if (dt != NULL) msgtail("(%s) ", dt->fs_file); if (host) msgtail("to %s on host %s\n", tape, host); else msgtail("to %s\n", tape); sync(); - if ((ret = sbget(diskfd, &sblock, STDSB)) != 0) { + if ((ret = sbget(diskfd, &sblock, UFS_STDSB, UFS_NOCSUM)) != 0) { switch (ret) { case ENOENT: warn("Cannot find file system superblock"); return (1); default: warn("Unable to read file system superblock"); return (1); } } dev_bsize = sblock->fs_fsize / fsbtodb(sblock, 1); dev_bshift = ffs(dev_bsize) - 1; if (dev_bsize != (1 << dev_bshift)) quit("dev_bsize (%ld) is not a power of 2", dev_bsize); tp_bshift = ffs(TP_BSIZE) - 1; if (TP_BSIZE != (1 << tp_bshift)) quit("TP_BSIZE (%d) is not a power of 2", TP_BSIZE); maxino = sblock->fs_ipg * sblock->fs_ncg; mapsize = roundup(howmany(maxino, CHAR_BIT), TP_BSIZE); usedinomap = (char *)calloc((unsigned) mapsize, sizeof(char)); dumpdirmap = (char *)calloc((unsigned) mapsize, sizeof(char)); dumpinomap = (char *)calloc((unsigned) mapsize, sizeof(char)); tapesize = 3 * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); nonodump = spcl.c_level < honorlevel; passno = 1; setproctitle("%s: pass 1: regular files", disk); msg("mapping (Pass I) [regular files]\n"); anydirskipped = mapfiles(maxino, &tapesize); passno = 2; setproctitle("%s: pass 2: directories", disk); msg("mapping (Pass II) [directories]\n"); while (anydirskipped) { anydirskipped = mapdirs(maxino, &tapesize); } if (pipeout || unlimited) { tapesize += 10; /* 10 trailer blocks */ msg("estimated %ld tape blocks.\n", tapesize); } else { double fetapes; if (blocksperfile) fetapes = (double) tapesize / blocksperfile; else if (cartridge) { /* Estimate number of tapes, assuming streaming stops at the end of each block written, and not in mid-block. Assume no erroneous blocks; this can be compensated for with an artificially low tape size. */ fetapes = ( (double) tapesize /* blocks */ * TP_BSIZE /* bytes/block */ * (1.0/density) /* 0.1" / byte " */ + (double) tapesize /* blocks */ * (1.0/ntrec) /* streaming-stops per block */ * 15.48 /* 0.1" / streaming-stop " */ ) * (1.0 / tsize ); /* tape / 0.1" " */ } else { /* Estimate number of tapes, for old fashioned 9-track tape */ int tenthsperirg = (density == 625) ? 3 : 7; fetapes = ( (double) tapesize /* blocks */ * TP_BSIZE /* bytes / block */ * (1.0/density) /* 0.1" / byte " */ + (double) tapesize /* blocks */ * (1.0/ntrec) /* IRG's / block */ * tenthsperirg /* 0.1" / IRG " */ ) * (1.0 / tsize ); /* tape / 0.1" " */ } etapes = fetapes; /* truncating assignment */ etapes++; /* count the dumped inodes map on each additional tape */ tapesize += (etapes - 1) * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); tapesize += etapes + 10; /* headers + 10 trailer blks */ msg("estimated %ld tape blocks on %3.2f tape(s).\n", tapesize, fetapes); } /* * If the user only wants an estimate of the number of * tapes, exit now. */ if (just_estimate) exit(0); /* * Allocate tape buffer. */ if (!alloctape()) quit( "can't allocate tape buffers - try a smaller blocking factor.\n"); startnewtape(1); (void)time((time_t *)&(tstart_writing)); dumpmap(usedinomap, TS_CLRI, maxino - 1); passno = 3; setproctitle("%s: pass 3: directories", disk); msg("dumping (Pass III) [directories]\n"); dirty = 0; /* XXX just to get gcc to shut up */ for (map = dumpdirmap, ino = 1; ino < maxino; ino++) { if (((ino - 1) % CHAR_BIT) == 0) /* map is offset by 1 */ dirty = *map++; else dirty >>= 1; if ((dirty & 1) == 0) continue; /* * Skip directory inodes deleted and maybe reallocated */ dp = getino(ino, &mode); if (mode != IFDIR) continue; (void)dumpino(dp, ino); } passno = 4; setproctitle("%s: pass 4: regular files", disk); msg("dumping (Pass IV) [regular files]\n"); for (map = dumpinomap, ino = 1; ino < maxino; ino++) { if (((ino - 1) % CHAR_BIT) == 0) /* map is offset by 1 */ dirty = *map++; else dirty >>= 1; if ((dirty & 1) == 0) continue; /* * Skip inodes deleted and reallocated as directories. */ dp = getino(ino, &mode); if (mode == IFDIR) continue; (void)dumpino(dp, ino); } (void)time((time_t *)&(tend_writing)); spcl.c_type = TS_END; for (i = 0; i < ntrec; i++) writeheader(maxino - 1); if (pipeout) msg("DUMP: %jd tape blocks\n", (intmax_t)spcl.c_tapea); else msg("DUMP: %jd tape blocks on %d volume%s\n", (intmax_t)spcl.c_tapea, spcl.c_volume, (spcl.c_volume == 1) ? "" : "s"); /* report dump performance, avoid division through zero */ if (tend_writing - tstart_writing == 0) msg("finished in less than a second\n"); else msg("finished in %jd seconds, throughput %jd KBytes/sec\n", (intmax_t)tend_writing - tstart_writing, (intmax_t)(spcl.c_tapea / (tend_writing - tstart_writing))); putdumptime(); trewind(); broadcast("DUMP IS DONE!\a\a\n"); msg("DUMP IS DONE\n"); Exit(X_FINOK); /* NOTREACHED */ } static void usage(void) { fprintf(stderr, "usage: dump [-0123456789acLnSu] [-B records] [-b blocksize] [-C cachesize]\n" " [-D dumpdates] [-d density] [-f file | -P pipecommand] [-h level]\n" " [-s feet] [-T date] filesystem\n" " dump -W | -w\n"); exit(X_STARTUP); } /* * Check to see if a disk is currently mounted. */ static char * getmntpt(char *name, int *mntflagsp) { long mntsize, i; struct statfs *mntbuf; mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); for (i = 0; i < mntsize; i++) { if (!strcmp(mntbuf[i].f_mntfromname, name)) { *mntflagsp = mntbuf[i].f_flags; return (mntbuf[i].f_mntonname); } } return (0); } /* * Pick up a numeric argument. It must be nonnegative and in the given * range (except that a vmax of 0 means unlimited). */ static long numarg(const char *meaning, long vmin, long vmax) { char *p; long val; val = strtol(optarg, &p, 10); if (*p) errx(1, "illegal %s -- %s", meaning, optarg); if (val < vmin || (vmax && val > vmax)) errx(1, "%s must be between %ld and %ld", meaning, vmin, vmax); return (val); } void sig(int signo) { switch(signo) { case SIGALRM: case SIGBUS: case SIGFPE: case SIGHUP: case SIGTERM: case SIGTRAP: if (pipeout) quit("Signal on pipe: cannot recover\n"); msg("Rewriting attempted as response to unknown signal.\n"); (void)fflush(stderr); (void)fflush(stdout); close_rewind(); exit(X_REWRITE); /* NOTREACHED */ case SIGSEGV: msg("SIGSEGV: ABORTING!\n"); (void)signal(SIGSEGV, SIG_DFL); (void)kill(0, SIGSEGV); /* NOTREACHED */ } } char * rawname(char *cp) { struct stat sb; /* * Ensure that the device passed in is a raw device. */ if (stat(cp, &sb) == 0 && (sb.st_mode & S_IFMT) == S_IFCHR) return (cp); /* * Since there's only one device type now, we can't construct any * better name, so we have to return NULL. */ return (NULL); } /* * obsolete -- * Change set of key letters and ordered arguments into something * getopt(3) will like. */ static void obsolete(int *argcp, char **argvp[]) { int argc, flags; char *ap, **argv, *flagsp, **nargv, *p; /* Setup. */ argv = *argvp; argc = *argcp; /* * Return if no arguments or first argument has leading * dash or slash. */ ap = argv[1]; if (argc == 1 || *ap == '-' || *ap == '/') return; /* Allocate space for new arguments. */ if ((*argvp = nargv = malloc((argc + 1) * sizeof(char *))) == NULL || (p = flagsp = malloc(strlen(ap) + 2)) == NULL) err(1, NULL); *nargv++ = *argv; argv += 2; for (flags = 0; *ap; ++ap) { switch (*ap) { case 'B': case 'b': case 'd': case 'f': case 'D': case 'C': case 'h': case 's': case 'T': if (*argv == NULL) { warnx("option requires an argument -- %c", *ap); usage(); } if ((nargv[0] = malloc(strlen(*argv) + 2 + 1)) == NULL) err(1, NULL); nargv[0][0] = '-'; nargv[0][1] = *ap; (void)strcpy(&nargv[0][2], *argv); ++argv; ++nargv; break; default: if (!flags) { *p++ = '-'; flags = 1; } *p++ = *ap; break; } } /* Terminate flags. */ if (flags) { *p = '\0'; *nargv++ = flagsp; } else free(flagsp); /* Copy remaining arguments. */ while ((*nargv++ = *argv++)); /* Update argument count. */ *argcp = nargv - *argvp - 1; } diff --git a/sbin/dumpfs/dumpfs.c b/sbin/dumpfs/dumpfs.c index 7f2acdf435ea..6a586ffb95ec 100644 --- a/sbin/dumpfs/dumpfs.c +++ b/sbin/dumpfs/dumpfs.c @@ -1,544 +1,541 @@ /* * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2009 Robert N. M. Watson * All rights reserved. * * This software was developed at the University of Cambridge Computer * Laboratory with support from a grant from Google, Inc. * * Copyright (c) 2002 Networks Associates Technology, Inc. * All rights reserved. * * This software was developed for the FreeBSD Project by Marshall * Kirk McKusick and Network Associates Laboratories, the Security * Research Division of Network Associates, Inc. under DARPA/SPAWAR * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS * research program. * * Copyright (c) 1983, 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1983, 1992, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)dumpfs.c 8.5 (Berkeley) 4/29/95"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define afs disk.d_fs #define acg disk.d_cg static struct uufsd disk; static int dumpfs(const char *, int); static int dumpfsid(void); static int dumpcg(void); static int dumpfreespace(const char *, int); static void dumpfreespacecg(int); static int marshal(const char *); static void pbits(void *, int); static void pblklist(void *, int, off_t, int); static void ufserr(const char *); static void usage(void) __dead2; int main(int argc, char *argv[]) { const char *name; int ch, dofreespace, domarshal, dolabel, dosb, eval; dofreespace = domarshal = dolabel = dosb = eval = 0; while ((ch = getopt(argc, argv, "lfms")) != -1) { switch (ch) { case 'f': dofreespace++; break; case 'm': domarshal = 1; break; case 'l': dolabel = 1; break; case 's': dosb = 1; break; case '?': default: usage(); } } argc -= optind; argv += optind; if (argc < 1) usage(); if (dofreespace && domarshal) usage(); if (dofreespace > 2) usage(); while ((name = *argv++) != NULL) { if (ufs_disk_fillout_blank(&disk, name) == -1) { ufserr(name); eval |= 1; continue; } - disk.d_sblockloc = STDSB_NOHASHFAIL; + disk.d_lookupflags |= UFS_NOHASHFAIL; if (sbread(&disk) == -1) { ufserr(name); eval |= 1; continue; } if (dofreespace) eval |= dumpfreespace(name, dofreespace); else if (domarshal) eval |= marshal(name); else if (dolabel) eval |= dumpfsid(); else eval |= dumpfs(name, dosb); ufs_disk_close(&disk); } exit(eval); } static int dumpfsid(void) { printf("%sufsid/%08x%08x\n", _PATH_DEV, afs.fs_id[0], afs.fs_id[1]); return 0; } static int dumpfs(const char *name, int dosb) { time_t fstime, fsmtime; int64_t fssize; int32_t fsflags; int i; switch (disk.d_ufs) { case 2: fssize = afs.fs_size; fstime = afs.fs_time; fsmtime = afs.fs_mtime; printf("magic\t%x (UFS2)\n", afs.fs_magic); printf("last mounted time\t%s", ctime(&fsmtime)); printf("last modified time\t%s", ctime(&fstime)); printf("superblock location\t%jd\tid\t[ %08x %08x ]\n", (intmax_t)afs.fs_sblockloc, afs.fs_id[0], afs.fs_id[1]); printf("ncg\t%d\tsize\t%jd\tblocks\t%jd\n", afs.fs_ncg, (intmax_t)fssize, (intmax_t)afs.fs_dsize); break; case 1: fssize = afs.fs_old_size; fstime = afs.fs_old_time; printf("magic\t%x (UFS1)\ttime\t%s", afs.fs_magic, ctime(&fstime)); printf("id\t[ %08x %08x ]\n", afs.fs_id[0], afs.fs_id[1]); printf("ncg\t%d\tsize\t%jd\tblocks\t%jd\n", afs.fs_ncg, (intmax_t)fssize, (intmax_t)afs.fs_dsize); break; default: goto err; } printf("bsize\t%d\tshift\t%d\tmask\t0x%08x\n", afs.fs_bsize, afs.fs_bshift, afs.fs_bmask); printf("fsize\t%d\tshift\t%d\tmask\t0x%08x\n", afs.fs_fsize, afs.fs_fshift, afs.fs_fmask); printf("frag\t%d\tshift\t%d\tfsbtodb\t%d\n", afs.fs_frag, afs.fs_fragshift, afs.fs_fsbtodb); printf("minfree\t%d%%\toptim\t%s\tsymlinklen %d\n", afs.fs_minfree, afs.fs_optim == FS_OPTSPACE ? "space" : "time", afs.fs_maxsymlinklen); switch (disk.d_ufs) { case 2: printf("%s %d\tmaxbpg\t%d\tmaxcontig %d\tcontigsumsize %d\n", "maxbsize", afs.fs_maxbsize, afs.fs_maxbpg, afs.fs_maxcontig, afs.fs_contigsumsize); printf("nbfree\t%jd\tndir\t%jd\tnifree\t%jd\tnffree\t%jd\n", (intmax_t)afs.fs_cstotal.cs_nbfree, (intmax_t)afs.fs_cstotal.cs_ndir, (intmax_t)afs.fs_cstotal.cs_nifree, (intmax_t)afs.fs_cstotal.cs_nffree); printf("bpg\t%d\tfpg\t%d\tipg\t%d\tunrefs\t%jd\n", afs.fs_fpg / afs.fs_frag, afs.fs_fpg, afs.fs_ipg, (intmax_t)afs.fs_unrefs); printf("nindir\t%d\tinopb\t%d\tmaxfilesize\t%ju\n", afs.fs_nindir, afs.fs_inopb, (uintmax_t)afs.fs_maxfilesize); printf("sbsize\t%d\tcgsize\t%d\tcsaddr\t%jd\tcssize\t%d\n", afs.fs_sbsize, afs.fs_cgsize, (intmax_t)afs.fs_csaddr, afs.fs_cssize); break; case 1: printf("maxbpg\t%d\tmaxcontig %d\tcontigsumsize %d\n", afs.fs_maxbpg, afs.fs_maxcontig, afs.fs_contigsumsize); printf("nbfree\t%d\tndir\t%d\tnifree\t%d\tnffree\t%d\n", afs.fs_old_cstotal.cs_nbfree, afs.fs_old_cstotal.cs_ndir, afs.fs_old_cstotal.cs_nifree, afs.fs_old_cstotal.cs_nffree); printf("cpg\t%d\tbpg\t%d\tfpg\t%d\tipg\t%d\n", afs.fs_old_cpg, afs.fs_fpg / afs.fs_frag, afs.fs_fpg, afs.fs_ipg); printf("nindir\t%d\tinopb\t%d\tnspf\t%d\tmaxfilesize\t%ju\n", afs.fs_nindir, afs.fs_inopb, afs.fs_old_nspf, (uintmax_t)afs.fs_maxfilesize); printf("sbsize\t%d\tcgsize\t%d\tcgoffset %d\tcgmask\t0x%08x\n", afs.fs_sbsize, afs.fs_cgsize, afs.fs_old_cgoffset, afs.fs_old_cgmask); printf("csaddr\t%jd\tcssize\t%d\n", (intmax_t)afs.fs_csaddr, afs.fs_cssize); printf("rotdelay %dms\trps\t%d\ttrackskew %d\tinterleave %d\n", afs.fs_old_rotdelay, afs.fs_old_rps, afs.fs_old_trackskew, afs.fs_old_interleave); printf("nsect\t%d\tnpsect\t%d\tspc\t%d\n", afs.fs_old_nsect, afs.fs_old_npsect, afs.fs_old_spc); break; default: goto err; } printf("sblkno\t%d\tcblkno\t%d\tiblkno\t%d\tdblkno\t%d\n", afs.fs_sblkno, afs.fs_cblkno, afs.fs_iblkno, afs.fs_dblkno); printf("cgrotor\t%d\tfmod\t%d\tronly\t%d\tclean\t%d\n", afs.fs_cgrotor, afs.fs_fmod, afs.fs_ronly, afs.fs_clean); printf("metaspace %jd\tavgfpdir %d\tavgfilesize %d\n", afs.fs_metaspace, afs.fs_avgfpdir, afs.fs_avgfilesize); printf("flags\t"); if (afs.fs_old_flags & FS_FLAGS_UPDATED) fsflags = afs.fs_flags; else fsflags = afs.fs_old_flags; if (fsflags == 0) printf("none"); if (fsflags & FS_UNCLEAN) printf("unclean "); if (fsflags & FS_DOSOFTDEP) printf("soft-updates%s ", (fsflags & FS_SUJ) ? "+journal" : ""); if (fsflags & FS_NEEDSFSCK) printf("needs-fsck-run "); if (fsflags & FS_INDEXDIRS) printf("indexed-directories "); if (fsflags & FS_ACLS) printf("acls "); if (fsflags & FS_MULTILABEL) printf("multilabel "); if (fsflags & FS_GJOURNAL) printf("gjournal "); if (fsflags & FS_FLAGS_UPDATED) printf("fs_flags-expanded "); if (fsflags & FS_NFS4ACLS) printf("nfsv4acls "); if (fsflags & FS_TRIM) printf("trim "); fsflags &= ~(FS_UNCLEAN | FS_DOSOFTDEP | FS_NEEDSFSCK | FS_METACKHASH | FS_ACLS | FS_MULTILABEL | FS_GJOURNAL | FS_FLAGS_UPDATED | FS_NFS4ACLS | FS_SUJ | FS_TRIM | FS_INDEXDIRS); if (fsflags != 0) printf("unknown-flags (%#x)", fsflags); putchar('\n'); if (afs.fs_flags & FS_METACKHASH) { printf("check hashes\t"); fsflags = afs.fs_metackhash; if (fsflags == 0) printf("none"); if (fsflags & CK_SUPERBLOCK) printf("superblock "); if (fsflags & CK_CYLGRP) printf("cylinder-groups "); if (fsflags & CK_INODE) printf("inodes "); if (fsflags & CK_INDIR) printf("indirect-blocks "); if (fsflags & CK_DIR) printf("directories "); } fsflags &= ~(CK_SUPERBLOCK | CK_CYLGRP | CK_INODE | CK_INDIR | CK_DIR); if (fsflags != 0) printf("unknown flags (%#x)", fsflags); putchar('\n'); printf("fsmnt\t%s\n", afs.fs_fsmnt); printf("volname\t%s\tswuid\t%ju\tprovidersize\t%ju\n", afs.fs_volname, (uintmax_t)afs.fs_swuid, (uintmax_t)afs.fs_providersize); printf("\ncs[].cs_(nbfree,ndir,nifree,nffree):\n\t"); - afs.fs_csp = calloc(1, afs.fs_cssize); - if (bread(&disk, fsbtodb(&afs, afs.fs_csaddr), afs.fs_csp, afs.fs_cssize) == -1) - goto err; for (i = 0; i < afs.fs_ncg; i++) { struct csum *cs = &afs.fs_cs(&afs, i); if (i && i % 4 == 0) printf("\n\t"); printf("(%d,%d,%d,%d) ", cs->cs_nbfree, cs->cs_ndir, cs->cs_nifree, cs->cs_nffree); } printf("\n"); if (fssize % afs.fs_fpg) { if (disk.d_ufs == 1) printf("cylinders in last group %d\n", howmany(afs.fs_old_size % afs.fs_fpg, afs.fs_old_spc / afs.fs_old_nspf)); printf("blocks in last group %ld\n\n", (long)((fssize % afs.fs_fpg) / afs.fs_frag)); } if (dosb) return (0); while ((i = cgread(&disk)) != 0) { if (i == -1 || dumpcg()) goto err; } return (0); err: ufserr(name); return (1); } static int dumpcg(void) { time_t cgtime; off_t cur; int i, j; printf("\ncg %d:\n", disk.d_lcg); cur = fsbtodb(&afs, cgtod(&afs, disk.d_lcg)) * disk.d_bsize; switch (disk.d_ufs) { case 2: cgtime = acg.cg_time; printf("magic\t%x\ttell\t%jx\ttime\t%s", acg.cg_magic, (intmax_t)cur, ctime(&cgtime)); printf("cgx\t%d\tndblk\t%d\tniblk\t%d\tinitiblk %d\tunrefs %d\n", acg.cg_cgx, acg.cg_ndblk, acg.cg_niblk, acg.cg_initediblk, acg.cg_unrefs); break; case 1: cgtime = acg.cg_old_time; printf("magic\t%x\ttell\t%jx\ttime\t%s", acg.cg_magic, (intmax_t)cur, ctime(&cgtime)); printf("cgx\t%d\tncyl\t%d\tniblk\t%d\tndblk\t%d\n", acg.cg_cgx, acg.cg_old_ncyl, acg.cg_old_niblk, acg.cg_ndblk); break; default: break; } printf("nbfree\t%d\tndir\t%d\tnifree\t%d\tnffree\t%d\n", acg.cg_cs.cs_nbfree, acg.cg_cs.cs_ndir, acg.cg_cs.cs_nifree, acg.cg_cs.cs_nffree); printf("rotor\t%d\tirotor\t%d\tfrotor\t%d\nfrsum", acg.cg_rotor, acg.cg_irotor, acg.cg_frotor); for (i = 1, j = 0; i < afs.fs_frag; i++) { printf("\t%d", acg.cg_frsum[i]); j += i * acg.cg_frsum[i]; } printf("\nsum of frsum: %d", j); if (afs.fs_contigsumsize > 0) { for (i = 1; i < afs.fs_contigsumsize; i++) { if ((i - 1) % 8 == 0) printf("\nclusters %d-%d:", i, MIN(afs.fs_contigsumsize - 1, i + 7)); printf("\t%d", cg_clustersum(&acg)[i]); } printf("\nclusters size %d and over: %d\n", afs.fs_contigsumsize, cg_clustersum(&acg)[afs.fs_contigsumsize]); printf("clusters free:\t"); pbits(cg_clustersfree(&acg), acg.cg_nclusterblks); } else printf("\n"); printf("inodes used:\t"); pbits(cg_inosused(&acg), afs.fs_ipg); printf("blks free:\t"); pbits(cg_blksfree(&acg), afs.fs_fpg); return (0); } static int dumpfreespace(const char *name, int fflag) { int i; while ((i = cgread(&disk)) != 0) { if (i == -1) goto err; dumpfreespacecg(fflag); } return (0); err: ufserr(name); return (1); } static void dumpfreespacecg(int fflag) { pblklist(cg_blksfree(&acg), afs.fs_fpg, disk.d_lcg * afs.fs_fpg, fflag); } static int marshal(const char *name) { struct fs *fs; fs = &disk.d_fs; printf("# newfs command for %s (%s)\n", name, disk.d_name); printf("newfs "); if (fs->fs_volname[0] != '\0') printf("-L %s ", fs->fs_volname); printf("-O %d ", disk.d_ufs); if (fs->fs_flags & FS_DOSOFTDEP) printf("-U "); printf("-a %d ", fs->fs_maxcontig); printf("-b %d ", fs->fs_bsize); /* -c is dumb */ printf("-d %d ", fs->fs_maxbsize); printf("-e %d ", fs->fs_maxbpg); printf("-f %d ", fs->fs_fsize); printf("-g %d ", fs->fs_avgfilesize); printf("-h %d ", fs->fs_avgfpdir); printf("-i %jd ", fragroundup(fs, lblktosize(fs, fragstoblks(fs, fs->fs_fpg)) / fs->fs_ipg)); if (fs->fs_flags & FS_SUJ) printf("-j "); if (fs->fs_flags & FS_GJOURNAL) printf("-J "); printf("-k %jd ", fs->fs_metaspace); if (fs->fs_flags & FS_MULTILABEL) printf("-l "); printf("-m %d ", fs->fs_minfree); /* -n unimplemented */ printf("-o "); switch (fs->fs_optim) { case FS_OPTSPACE: printf("space "); break; case FS_OPTTIME: printf("time "); break; default: printf("unknown "); break; } /* -p..r unimplemented */ printf("-s %jd ", (intmax_t)fsbtodb(fs, fs->fs_size)); if (fs->fs_flags & FS_TRIM) printf("-t "); printf("%s ", disk.d_name); printf("\n"); return 0; } static void pbits(void *vp, int max) { int i; char *p; int count, j; for (count = i = 0, p = vp; i < max; i++) if (isset(p, i)) { if (count) printf(",%s", count % 6 ? " " : "\n\t"); count++; printf("%d", i); j = i; while ((i+1) __FBSDID("$FreeBSD$"); #include #include #include #define FSTYPENAMES #include #include #include #include #include #include #include #include #include #include #include #include #include "fsck.h" struct inoinfo **inphead, **inpsort; /* info about all inodes */ struct bufarea asblk; #define altsblock (*asblk.b_un.b_fs) #define POWEROF2(num) (((num) & ((num) - 1)) == 0) static int calcsb(char *dev, int devfd, struct fs *fs); static void saverecovery(int readfd, int writefd); static int chkrecovery(int devfd); /* * Read in a superblock finding an alternate if necessary. * Return 1 if successful, 0 if unsuccessful, -1 if file system * is already clean (ckclean and preen mode only). */ int setup(char *dev) { long cg, bmapsize; struct fs proto; /* * We are expected to have an open file descriptor */ if (fsreadfd < 0) return (0); /* * If we do not yet have a superblock, read it in looking * for alternates if necessary. */ if (havesb == 0 && readsb(1) == 0) { skipclean = 0; if (bflag || preen || calcsb(dev, fsreadfd, &proto) == 0) return(0); if (reply("LOOK FOR ALTERNATE SUPERBLOCKS") == 0) return (0); for (cg = 0; cg < proto.fs_ncg; cg++) { bflag = fsbtodb(&proto, cgsblock(&proto, cg)); if (readsb(0) != 0) break; } if (cg >= proto.fs_ncg) { printf("SEARCH FOR ALTERNATE SUPER-BLOCK FAILED. " "YOU MUST USE THE\n-b OPTION TO FSCK TO SPECIFY " "THE LOCATION OF AN ALTERNATE\nSUPER-BLOCK TO " "SUPPLY NEEDED INFORMATION; SEE fsck_ffs(8).\n"); bflag = 0; return(0); } pwarn("USING ALTERNATE SUPERBLOCK AT %jd\n", bflag); bflag = 0; } if (preen == 0) printf("** %s", dev); if (bkgrdflag == 0 && (nflag || (fswritefd = open(dev, O_WRONLY)) < 0)) { fswritefd = -1; if (preen) pfatal("NO WRITE ACCESS"); printf(" (NO WRITE)"); } if (preen == 0) printf("\n"); if (sbhashfailed != 0) { pwarn("SUPERBLOCK CHECK HASH FAILED"); if (fswritefd == -1) pwarn("OPENED READONLY SO CANNOT CORRECT CHECK HASH\n"); else if (preen || reply("CORRECT CHECK HASH") != 0) { if (preen) printf(" (CORRECTED)\n"); sblock.fs_clean = 0; sbdirty(); } } if (skipclean && ckclean && sblock.fs_clean) { pwarn("FILE SYSTEM CLEAN; SKIPPING CHECKS\n"); return (-1); } maxfsblock = sblock.fs_size; maxino = sblock.fs_ncg * sblock.fs_ipg; /* * Check and potentially fix certain fields in the super block. */ if (sblock.fs_optim != FS_OPTTIME && sblock.fs_optim != FS_OPTSPACE) { pfatal("UNDEFINED OPTIMIZATION IN SUPERBLOCK"); if (reply("SET TO DEFAULT") == 1) { sblock.fs_optim = FS_OPTTIME; sbdirty(); } } if ((sblock.fs_minfree < 0 || sblock.fs_minfree > 99)) { pfatal("IMPOSSIBLE MINFREE=%d IN SUPERBLOCK", sblock.fs_minfree); if (reply("SET TO DEFAULT") == 1) { sblock.fs_minfree = 10; sbdirty(); } } if (sblock.fs_magic == FS_UFS1_MAGIC && sblock.fs_old_inodefmt < FS_44INODEFMT) { pwarn("Format of file system is too old.\n"); pwarn("Must update to modern format using a version of fsck\n"); pfatal("from before 2002 with the command ``fsck -c 2''\n"); exit(EEXIT); } if ((asblk.b_flags & B_DIRTY) != 0 && !bflag) { memmove(&altsblock, &sblock, (size_t)sblock.fs_sbsize); flush(fswritefd, &asblk); } if (preen == 0 && yflag == 0 && sblock.fs_magic == FS_UFS2_MAGIC && fswritefd != -1 && chkrecovery(fsreadfd) == 0 && reply("SAVE DATA TO FIND ALTERNATE SUPERBLOCKS") != 0) saverecovery(fsreadfd, fswritefd); /* * allocate and initialize the necessary maps */ bmapsize = roundup(howmany(maxfsblock, CHAR_BIT), sizeof(short)); blockmap = Calloc((unsigned)bmapsize, sizeof (char)); if (blockmap == NULL) { printf("cannot alloc %u bytes for blockmap\n", (unsigned)bmapsize); goto badsb; } inostathead = Calloc(sblock.fs_ncg, sizeof(struct inostatlist)); if (inostathead == NULL) { printf("cannot alloc %u bytes for inostathead\n", (unsigned)(sizeof(struct inostatlist) * (sblock.fs_ncg))); goto badsb; } numdirs = MAX(sblock.fs_cstotal.cs_ndir, 128); dirhash = numdirs; inplast = 0; listmax = numdirs + 10; inpsort = (struct inoinfo **)Calloc(listmax, sizeof(struct inoinfo *)); inphead = (struct inoinfo **)Calloc(numdirs, sizeof(struct inoinfo *)); if (inpsort == NULL || inphead == NULL) { printf("cannot alloc %ju bytes for inphead\n", (uintmax_t)numdirs * sizeof(struct inoinfo *)); goto badsb; } bufinit(); if (sblock.fs_flags & FS_DOSOFTDEP) usedsoftdep = 1; else usedsoftdep = 0; return (1); badsb: ckfini(0); return (0); } /* * Open a device or file to be checked by fsck. */ int openfilesys(char *dev) { struct stat statb; int saved_fsreadfd; if (stat(dev, &statb) < 0) return (0); if ((statb.st_mode & S_IFMT) != S_IFCHR && (statb.st_mode & S_IFMT) != S_IFBLK) { if (bkgrdflag != 0 && (statb.st_flags & SF_SNAPSHOT) == 0) { pfatal("BACKGROUND FSCK LACKS A SNAPSHOT\n"); exit(EEXIT); } if (bkgrdflag != 0) { cursnapshot = statb.st_ino; } else { pfatal("%s IS NOT A DISK DEVICE\n", dev); if (reply("CONTINUE") == 0) return (0); } } saved_fsreadfd = fsreadfd; if ((fsreadfd = open(dev, O_RDONLY)) < 0) { fsreadfd = saved_fsreadfd; return (0); } if (saved_fsreadfd != -1) close(saved_fsreadfd); return (1); } /* * Read in the super block and its summary info. */ int readsb(int listerr) { off_t super; - int bad, ret; + int bad, ret, flags; struct fs *fs; - super = bflag ? bflag * dev_bsize : - sbhashfailed ? STDSB_NOHASHFAIL_NOMSG : STDSB_NOMSG; + super = bflag ? bflag * dev_bsize : UFS_STDSB; + flags = sbhashfailed ? UFS_NOHASHFAIL | UFS_NOMSG : UFS_NOMSG; readcnt[sblk.b_type]++; - while ((ret = sbget(fsreadfd, &fs, super)) != 0) { + while ((ret = sbget(fsreadfd, &fs, super, flags)) != 0) { switch (ret) { case EINTEGRITY: - if (bflag || super == STDSB_NOHASHFAIL_NOMSG) + if (bflag || (super == UFS_STDSB && + flags == (UFS_NOHASHFAIL | UFS_NOMSG))) return (0); - super = STDSB_NOHASHFAIL_NOMSG; + super = UFS_STDSB; + flags = UFS_NOHASHFAIL | UFS_NOMSG; sbhashfailed = 1; continue; case ENOENT: if (bflag) printf("%jd is not a file system " "superblock\n", super / dev_bsize); else printf("Cannot find file system " "superblock\n"); return (0); case EIO: default: printf("I/O error reading %jd\n", super / dev_bsize); return (0); } } memcpy(&sblock, fs, fs->fs_sbsize); free(fs); /* * Compute block size that the file system is based on, * according to fsbtodb, and adjust superblock block number * so we can tell if this is an alternate later. */ dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1); sblk.b_bno = sblock.fs_sblockactualloc / dev_bsize; sblk.b_size = SBLOCKSIZE; /* * Compare all fields that should not differ in alternate super block. * When an alternate super-block is specified this check is skipped. */ if (bflag) goto out; getblk(&asblk, cgsblock(&sblock, sblock.fs_ncg - 1), sblock.fs_sbsize); if (asblk.b_errs) return (0); bad = 0; #define CHK(x, y) \ if (altsblock.x != sblock.x) { \ bad++; \ if (listerr && debug) \ printf("SUPER BLOCK VS ALTERNATE MISMATCH %s: " y " vs " y "\n", \ #x, (intmax_t)sblock.x, (intmax_t)altsblock.x); \ } CHK(fs_sblkno, "%jd"); CHK(fs_cblkno, "%jd"); CHK(fs_iblkno, "%jd"); CHK(fs_dblkno, "%jd"); CHK(fs_ncg, "%jd"); CHK(fs_bsize, "%jd"); CHK(fs_fsize, "%jd"); CHK(fs_frag, "%jd"); CHK(fs_bmask, "%#jx"); CHK(fs_fmask, "%#jx"); CHK(fs_bshift, "%jd"); CHK(fs_fshift, "%jd"); CHK(fs_fragshift, "%jd"); CHK(fs_fsbtodb, "%jd"); CHK(fs_sbsize, "%jd"); CHK(fs_nindir, "%jd"); CHK(fs_inopb, "%jd"); CHK(fs_cssize, "%jd"); CHK(fs_ipg, "%jd"); CHK(fs_fpg, "%jd"); CHK(fs_magic, "%#jx"); #undef CHK if (bad) { if (listerr == 0) return (0); if (preen) printf("%s: ", cdevname); printf( "VALUES IN SUPER BLOCK LSB=%jd DISAGREE WITH THOSE IN\n" "LAST ALTERNATE LSB=%jd\n", sblk.b_bno, asblk.b_bno); if (reply("IGNORE ALTERNATE SUPER BLOCK") == 0) return (0); } out: /* * If not yet done, update UFS1 superblock with new wider fields. */ if (sblock.fs_magic == FS_UFS1_MAGIC && sblock.fs_maxbsize != sblock.fs_bsize) { sblock.fs_maxbsize = sblock.fs_bsize; sblock.fs_time = sblock.fs_old_time; sblock.fs_size = sblock.fs_old_size; sblock.fs_dsize = sblock.fs_old_dsize; sblock.fs_csaddr = sblock.fs_old_csaddr; sblock.fs_cstotal.cs_ndir = sblock.fs_old_cstotal.cs_ndir; sblock.fs_cstotal.cs_nbfree = sblock.fs_old_cstotal.cs_nbfree; sblock.fs_cstotal.cs_nifree = sblock.fs_old_cstotal.cs_nifree; sblock.fs_cstotal.cs_nffree = sblock.fs_old_cstotal.cs_nffree; } havesb = 1; return (1); } void sblock_init(void) { fsreadfd = -1; fswritefd = -1; fsmodified = 0; lfdir = 0; initbarea(&sblk, BT_SUPERBLK); initbarea(&asblk, BT_SUPERBLK); sblk.b_un.b_buf = Malloc(SBLOCKSIZE); asblk.b_un.b_buf = Malloc(SBLOCKSIZE); if (sblk.b_un.b_buf == NULL || asblk.b_un.b_buf == NULL) errx(EEXIT, "cannot allocate space for superblock"); dev_bsize = secsize = DEV_BSIZE; } /* * Calculate a prototype superblock based on information in the boot area. * When done the cgsblock macro can be calculated and the fs_ncg field * can be used. Do NOT attempt to use other macros without verifying that * their needed information is available! */ static int calcsb(char *dev, int devfd, struct fs *fs) { struct fsrecovery *fsr; char *fsrbuf; u_int secsize; /* * We need fragments-per-group and the partition-size. * * Newfs stores these details at the end of the boot block area * at the start of the filesystem partition. If they have been * overwritten by a boot block, we fail. But usually they are * there and we can use them. */ if (ioctl(devfd, DIOCGSECTORSIZE, &secsize) == -1) return (0); fsrbuf = Malloc(secsize); if (fsrbuf == NULL) errx(EEXIT, "calcsb: cannot allocate recovery buffer"); if (blread(devfd, fsrbuf, (SBLOCK_UFS2 - secsize) / dev_bsize, secsize) != 0) { free(fsrbuf); return (0); } fsr = (struct fsrecovery *)&fsrbuf[secsize - sizeof *fsr]; if (fsr->fsr_magic != FS_UFS2_MAGIC) { free(fsrbuf); return (0); } memset(fs, 0, sizeof(struct fs)); fs->fs_fpg = fsr->fsr_fpg; fs->fs_fsbtodb = fsr->fsr_fsbtodb; fs->fs_sblkno = fsr->fsr_sblkno; fs->fs_magic = fsr->fsr_magic; fs->fs_ncg = fsr->fsr_ncg; free(fsrbuf); return (1); } /* * Check to see if recovery information exists. * Return 1 if it exists or cannot be created. * Return 0 if it does not exist and can be created. */ static int chkrecovery(int devfd) { struct fsrecovery *fsr; char *fsrbuf; u_int secsize, rdsize; /* * Could not determine if backup material exists, so do not * offer to create it. */ fsrbuf = NULL; rdsize = sblock.fs_fsize; if (ioctl(devfd, DIOCGSECTORSIZE, &secsize) == -1 || rdsize % secsize != 0 || (fsrbuf = Malloc(rdsize)) == NULL || blread(devfd, fsrbuf, (SBLOCK_UFS2 - rdsize) / dev_bsize, rdsize) != 0) { free(fsrbuf); return (1); } /* * Recovery material has already been created, so do not * need to create it again. */ fsr = (struct fsrecovery *)&fsrbuf[rdsize - sizeof *fsr]; if (fsr->fsr_magic == FS_UFS2_MAGIC) { free(fsrbuf); return (1); } /* * Recovery material has not been created and can be if desired. */ free(fsrbuf); return (0); } /* * Read the last filesystem-size piece of the boot block, replace the * last 20 bytes with the recovery information, then write it back. * The recovery information only works for UFS2 filesystems. */ static void saverecovery(int readfd, int writefd) { struct fsrecovery *fsr; char *fsrbuf; u_int secsize, rdsize; fsrbuf = NULL; rdsize = sblock.fs_fsize; if (sblock.fs_magic != FS_UFS2_MAGIC || ioctl(readfd, DIOCGSECTORSIZE, &secsize) == -1 || rdsize % secsize != 0 || (fsrbuf = Malloc(rdsize)) == NULL || blread(readfd, fsrbuf, (SBLOCK_UFS2 - rdsize) / dev_bsize, rdsize) != 0) { printf("RECOVERY DATA COULD NOT BE CREATED\n"); free(fsrbuf); return; } fsr = (struct fsrecovery *)&fsrbuf[rdsize - sizeof *fsr]; fsr->fsr_magic = sblock.fs_magic; fsr->fsr_fpg = sblock.fs_fpg; fsr->fsr_fsbtodb = sblock.fs_fsbtodb; fsr->fsr_sblkno = sblock.fs_sblkno; fsr->fsr_ncg = sblock.fs_ncg; blwrite(writefd, fsrbuf, (SBLOCK_UFS2 - rdsize) / dev_bsize, rdsize); free(fsrbuf); } diff --git a/sbin/fsirand/fsirand.c b/sbin/fsirand/fsirand.c index c4db8848f18b..90305416f556 100644 --- a/sbin/fsirand/fsirand.c +++ b/sbin/fsirand/fsirand.c @@ -1,240 +1,240 @@ /* $OpenBSD: fsirand.c,v 1.9 1997/02/28 00:46:33 millert Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 1997 Todd C. Miller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Todd C. Miller. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void usage(void) __dead2; int fsirand(char *); static int printonly = 0, force = 0, ignorelabel = 0; int main(int argc, char *argv[]) { int n, ex = 0; struct rlimit rl; while ((n = getopt(argc, argv, "bfp")) != -1) { switch (n) { case 'b': ignorelabel++; break; case 'p': printonly++; break; case 'f': force++; break; default: usage(); } } if (argc - optind < 1) usage(); /* Increase our data size to the max */ if (getrlimit(RLIMIT_DATA, &rl) == 0) { rl.rlim_cur = rl.rlim_max; if (setrlimit(RLIMIT_DATA, &rl) < 0) warn("can't get resource limit to max data size"); } else warn("can't get resource limit for data size"); for (n = optind; n < argc; n++) { if (argc - optind != 1) (void)puts(argv[n]); ex += fsirand(argv[n]); if (n < argc - 1) putchar('\n'); } exit(ex); } int fsirand(char *device) { struct ufs1_dinode *dp1; struct ufs2_dinode *dp2; caddr_t inodebuf; ssize_t ibufsize; struct fs *sblock; ino_t inumber; ufs2_daddr_t dblk; - int devfd, n, cg, ret; + int devfd, n, cg; u_int32_t bsize = DEV_BSIZE; if ((devfd = open(device, printonly ? O_RDONLY : O_RDWR)) < 0) { warn("can't open %s", device); return (1); } dp1 = NULL; dp2 = NULL; /* Read in master superblock */ - if ((ret = sbget(devfd, &sblock, STDSB)) != 0) { - switch (ret) { + if ((errno = sbget(devfd, &sblock, UFS_STDSB, UFS_NOCSUM)) != 0) { + switch (errno) { case ENOENT: - warn("Cannot find file system superblock"); + warnx("Cannot find file system superblock"); return (1); default: warn("Unable to read file system superblock"); return (1); } } /* * Check for unclean filesystem. */ if (sblock->fs_clean == 0 || (sblock->fs_flags & (FS_UNCLEAN | FS_NEEDSFSCK)) != 0) errx(1, "%s is not clean - run fsck.\n", device); if (sblock->fs_magic == FS_UFS1_MAGIC && sblock->fs_old_inodefmt < FS_44INODEFMT) { warnx("file system format is too old, sorry"); return (1); } if (!force && !printonly && sblock->fs_clean != 1) { warnx("file system is not clean, fsck %s first", device); return (1); } /* XXX - should really cap buffer at 512kb or so */ if (sblock->fs_magic == FS_UFS1_MAGIC) ibufsize = sizeof(struct ufs1_dinode) * sblock->fs_ipg; else ibufsize = sizeof(struct ufs2_dinode) * sblock->fs_ipg; if ((inodebuf = malloc(ibufsize)) == NULL) errx(1, "can't allocate memory for inode buffer"); if (printonly && (sblock->fs_id[0] || sblock->fs_id[1])) { if (sblock->fs_id[0]) (void)printf("%s was randomized on %s", device, ctime((void *)&(sblock->fs_id[0]))); (void)printf("fsid: %x %x\n", sblock->fs_id[0], sblock->fs_id[1]); } /* Randomize fs_id unless old 4.2BSD file system */ if (!printonly) { /* Randomize fs_id and write out new sblock and backups */ sblock->fs_id[0] = (u_int32_t)time(NULL); sblock->fs_id[1] = arc4random(); if (sbput(devfd, sblock, sblock->fs_ncg) != 0) { warn("could not write updated superblock"); return (1); } } /* For each cylinder group, randomize inodes and update backup sblock */ for (cg = 0, inumber = UFS_ROOTINO; cg < (int)sblock->fs_ncg; cg++) { /* Read in inodes, then print or randomize generation nums */ dblk = fsbtodb(sblock, ino_to_fsba(sblock, inumber)); if (lseek(devfd, (off_t)dblk * bsize, SEEK_SET) < 0) { warn("can't seek to %jd", (intmax_t)dblk * bsize); return (1); } else if ((n = read(devfd, inodebuf, ibufsize)) != ibufsize) { warnx("can't read inodes: %s", (n < ibufsize) ? "short read" : strerror(errno)); return (1); } dp1 = (struct ufs1_dinode *)(void *)inodebuf; dp2 = (struct ufs2_dinode *)(void *)inodebuf; for (n = cg > 0 ? 0 : UFS_ROOTINO; n < (int)sblock->fs_ipg; n++, inumber++) { if (printonly) { (void)printf("ino %ju gen %08x\n", (uintmax_t)inumber, sblock->fs_magic == FS_UFS1_MAGIC ? dp1->di_gen : dp2->di_gen); } else if (sblock->fs_magic == FS_UFS1_MAGIC) { dp1->di_gen = arc4random(); dp1++; } else { dp2->di_gen = arc4random(); ffs_update_dinode_ckhash(sblock, dp2); dp2++; } } /* Write out modified inodes */ if (!printonly) { if (lseek(devfd, (off_t)dblk * bsize, SEEK_SET) < 0) { warn("can't seek to %jd", (intmax_t)dblk * bsize); return (1); } else if ((n = write(devfd, inodebuf, ibufsize)) != ibufsize) { warnx("can't write inodes: %s", (n != ibufsize) ? "short write" : strerror(errno)); return (1); } } } (void)close(devfd); return(0); } static void usage(void) { (void)fprintf(stderr, "usage: fsirand [-b] [-f] [-p] special [special ...]\n"); exit(1); } diff --git a/sbin/growfs/growfs.c b/sbin/growfs/growfs.c index 69e6f04dd4c2..4a8d935d91c7 100644 --- a/sbin/growfs/growfs.c +++ b/sbin/growfs/growfs.c @@ -1,1771 +1,1771 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1980, 1989, 1993 The Regents of the University of California. * Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz * Copyright (c) 2012 The FreeBSD Foundation * All rights reserved. * * This code is derived from software contributed to Berkeley by * Christoph Herrmann and Thomas-Henning von Kamptz, Munich and Frankfurt. * * Portions of this software were developed by Edward Tomasz Napierala * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgment: * This product includes software developed by the University of * California, Berkeley and its contributors, as well as Christoph * Herrmann and Thomas-Henning von Kamptz. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $TSHeader: src/sbin/growfs/growfs.c,v 1.5 2000/12/12 19:31:00 tomsoft Exp $ * */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz\n\ Copyright (c) 1980, 1989, 1993 The Regents of the University of California.\n\ All rights reserved.\n"; #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "debug.h" #ifdef FS_DEBUG int _dbg_lvl_ = (DL_INFO); /* DL_TRC */ #endif /* FS_DEBUG */ static union { struct fs fs; char pad[SBLOCKSIZE]; } fsun1, fsun2; #define sblock fsun1.fs /* the new superblock */ #define osblock fsun2.fs /* the old superblock */ static union { struct cg cg; char pad[MAXBSIZE]; } cgun1, cgun2; #define acg cgun1.cg /* a cylinder cgroup (new) */ #define aocg cgun2.cg /* an old cylinder group */ static struct csum *fscs; /* cylinder summary */ static void growfs(int, int, unsigned int); static void rdfs(ufs2_daddr_t, size_t, void *, int); static void wtfs(ufs2_daddr_t, size_t, void *, int, unsigned int); static int charsperline(void); static void usage(void); static int isblock(struct fs *, unsigned char *, int); static void clrblock(struct fs *, unsigned char *, int); static void setblock(struct fs *, unsigned char *, int); static void initcg(int, time_t, int, unsigned int); static void updjcg(int, time_t, int, int, unsigned int); static void updcsloc(time_t, int, int, unsigned int); static void frag_adjust(ufs2_daddr_t, int); static void updclst(int); static void mount_reload(const struct statfs *stfs); static void cgckhash(struct cg *); /* * Here we actually start growing the file system. We basically read the * cylinder summary from the first cylinder group as we want to update * this on the fly during our various operations. First we handle the * changes in the former last cylinder group. Afterwards we create all new * cylinder groups. Now we handle the cylinder group containing the * cylinder summary which might result in a relocation of the whole * structure. In the end we write back the updated cylinder summary, the * new superblock, and slightly patched versions of the super block * copies. */ static void growfs(int fsi, int fso, unsigned int Nflag) { DBG_FUNC("growfs") time_t modtime; uint cylno; int i, j, width; char tmpbuf[100]; DBG_ENTER; time(&modtime); /* * Get the cylinder summary into the memory. */ fscs = (struct csum *)calloc((size_t)1, (size_t)sblock.fs_cssize); if (fscs == NULL) errx(1, "calloc failed"); memcpy(fscs, osblock.fs_csp, osblock.fs_cssize); free(osblock.fs_csp); osblock.fs_csp = NULL; sblock.fs_csp = fscs; #ifdef FS_DEBUG { struct csum *dbg_csp; u_int32_t dbg_csc; char dbg_line[80]; dbg_csp = fscs; for (dbg_csc = 0; dbg_csc < osblock.fs_ncg; dbg_csc++) { snprintf(dbg_line, sizeof(dbg_line), "%d. old csum in old location", dbg_csc); DBG_DUMP_CSUM(&osblock, dbg_line, dbg_csp++); } } #endif /* FS_DEBUG */ DBG_PRINT0("fscs read\n"); /* * Do all needed changes in the former last cylinder group. */ updjcg(osblock.fs_ncg - 1, modtime, fsi, fso, Nflag); /* * Dump out summary information about file system. */ #ifdef FS_DEBUG #define B2MBFACTOR (1 / (1024.0 * 1024.0)) printf("growfs: %.1fMB (%jd sectors) block size %d, fragment size %d\n", (float)sblock.fs_size * sblock.fs_fsize * B2MBFACTOR, (intmax_t)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); if (sblock.fs_flags & FS_DOSOFTDEP) printf("\twith soft updates\n"); #undef B2MBFACTOR #endif /* FS_DEBUG */ /* * Now build the cylinders group blocks and * then print out indices of cylinder groups. */ printf("super-block backups (for fsck_ffs -b #) at:\n"); i = 0; width = charsperline(); /* * Iterate for only the new cylinder groups. */ for (cylno = osblock.fs_ncg; cylno < sblock.fs_ncg; cylno++) { initcg(cylno, modtime, fso, Nflag); j = sprintf(tmpbuf, " %jd%s", (intmax_t)fsbtodb(&sblock, cgsblock(&sblock, cylno)), cylno < (sblock.fs_ncg - 1) ? "," : "" ); if (i + j >= width) { printf("\n"); i = 0; } i += j; printf("%s", tmpbuf); fflush(stdout); } printf("\n"); /* * Do all needed changes in the first cylinder group. * allocate blocks in new location */ updcsloc(modtime, fsi, fso, Nflag); /* * Clean up the dynamic fields in our superblock. * * XXX * The following fields are currently distributed from the superblock * to the copies: * fs_minfree * fs_rotdelay * fs_maxcontig * fs_maxbpg * fs_minfree, * fs_optim * fs_flags * * We probably should rather change the summary for the cylinder group * statistics here to the value of what would be in there, if the file * system were created initially with the new size. Therefor we still * need to find an easy way of calculating that. * Possibly we can try to read the first superblock copy and apply the * "diffed" stats between the old and new superblock by still copying * certain parameters onto that. */ sblock.fs_time = modtime; sblock.fs_fmod = 0; sblock.fs_clean = 1; sblock.fs_ronly = 0; sblock.fs_cgrotor = 0; sblock.fs_state = 0; memset((void *)&sblock.fs_fsmnt, 0, sizeof(sblock.fs_fsmnt)); /* * Now write the new superblock, its summary information, * and all the alternates back to disk. */ if (!Nflag && sbput(fso, &sblock, sblock.fs_ncg) != 0) errc(2, EIO, "could not write updated superblock"); DBG_PRINT0("fscs written\n"); #ifdef FS_DEBUG { struct csum *dbg_csp; u_int32_t dbg_csc; char dbg_line[80]; dbg_csp = fscs; for (dbg_csc = 0; dbg_csc < sblock.fs_ncg; dbg_csc++) { snprintf(dbg_line, sizeof(dbg_line), "%d. new csum in new location", dbg_csc); DBG_DUMP_CSUM(&sblock, dbg_line, dbg_csp++); } } #endif /* FS_DEBUG */ DBG_PRINT0("sblock written\n"); DBG_DUMP_FS(&sblock, "new initial sblock"); DBG_PRINT0("sblock copies written\n"); DBG_DUMP_FS(&sblock, "new other sblocks"); DBG_LEAVE; return; } /* * This creates a new cylinder group structure, for more details please see * the source of newfs(8), as this function is taken over almost unchanged. * As this is never called for the first cylinder group, the special * provisions for that case are removed here. */ static void initcg(int cylno, time_t modtime, int fso, unsigned int Nflag) { DBG_FUNC("initcg") static caddr_t iobuf; static long iobufsize; long blkno, start; ino_t ino; ufs2_daddr_t i, cbase, dmax; struct ufs1_dinode *dp1; struct ufs2_dinode *dp2; struct csum *cs; uint j, d, dupper, dlower; if (iobuf == NULL) { iobufsize = 2 * sblock.fs_bsize; if ((iobuf = malloc(iobufsize)) == NULL) errx(37, "panic: cannot allocate I/O buffer"); memset(iobuf, '\0', iobufsize); } /* * 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) /* XXX fscs may be relocated */ dupper += howmany(sblock.fs_cssize, sblock.fs_fsize); cs = &fscs[cylno]; memset(&acg, 0, sblock.fs_cgsize); acg.cg_time = modtime; acg.cg_magic = CG_MAGIC; acg.cg_cgx = cylno; acg.cg_niblk = sblock.fs_ipg; acg.cg_initediblk = MIN(sblock.fs_ipg, 2 * INOPB(&sblock)); acg.cg_ndblk = dmax - cbase; if (sblock.fs_contigsumsize > 0) acg.cg_nclusterblks = acg.cg_ndblk / sblock.fs_frag; start = &acg.cg_space[0] - (u_char *)(&acg.cg_firstfield); if (sblock.fs_magic == FS_UFS2_MAGIC) { acg.cg_iusedoff = start; } 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); acg.cg_nextfreeoff = acg.cg_freeoff + howmany(sblock.fs_fpg, CHAR_BIT); if (sblock.fs_contigsumsize > 0) { acg.cg_clustersumoff = roundup(acg.cg_nextfreeoff, sizeof(u_int32_t)); acg.cg_clustersumoff -= sizeof(u_int32_t); acg.cg_clusteroff = acg.cg_clustersumoff + (sblock.fs_contigsumsize + 1) * sizeof(u_int32_t); acg.cg_nextfreeoff = acg.cg_clusteroff + howmany(fragstoblks(&sblock, sblock.fs_fpg), CHAR_BIT); } if (acg.cg_nextfreeoff > (unsigned)sblock.fs_cgsize) { /* * This should never happen as we would have had that panic * already on file system creation */ errx(37, "panic: cylinder group too big"); } acg.cg_cs.cs_nifree += sblock.fs_ipg; if (cylno == 0) for (ino = 0; ino < UFS_ROOTINO; ino++) { setbit(cg_inosused(&acg), ino); acg.cg_cs.cs_nifree--; } /* * Initialize the initial inode blocks. */ dp1 = (struct ufs1_dinode *)(void *)iobuf; dp2 = (struct ufs2_dinode *)(void *)iobuf; for (i = 0; i < acg.cg_initediblk; i++) { if (sblock.fs_magic == FS_UFS1_MAGIC) { dp1->di_gen = arc4random(); dp1++; } else { dp2->di_gen = arc4random(); dp2++; } } wtfs(fsbtodb(&sblock, cgimin(&sblock, cylno)), iobufsize, iobuf, fso, Nflag); /* * For the old file system, we have to initialize all the inodes. */ if (sblock.fs_magic == FS_UFS1_MAGIC && sblock.fs_ipg > 2 * INOPB(&sblock)) { for (i = 2 * sblock.fs_frag; i < sblock.fs_ipg / INOPF(&sblock); i += sblock.fs_frag) { dp1 = (struct ufs1_dinode *)(void *)iobuf; for (j = 0; j < INOPB(&sblock); j++) { dp1->di_gen = arc4random(); dp1++; } wtfs(fsbtodb(&sblock, cgimin(&sblock, cylno) + i), sblock.fs_bsize, iobuf, fso, Nflag); } } if (cylno > 0) { /* * In cylno 0, beginning space is reserved * for boot and super blocks. */ for (d = 0; d < dlower; d += sblock.fs_frag) { blkno = d / sblock.fs_frag; setblock(&sblock, cg_blksfree(&acg), blkno); if (sblock.fs_contigsumsize > 0) setbit(cg_clustersfree(&acg), blkno); acg.cg_cs.cs_nbfree++; } sblock.fs_dsize += dlower; } sblock.fs_dsize += acg.cg_ndblk - dupper; sblock.fs_old_dsize = sblock.fs_dsize; if ((i = dupper % sblock.fs_frag)) { acg.cg_frsum[sblock.fs_frag - i]++; for (d = dupper + sblock.fs_frag - i; dupper < d; dupper++) { setbit(cg_blksfree(&acg), dupper); acg.cg_cs.cs_nffree++; } } for (d = dupper; d + sblock.fs_frag <= acg.cg_ndblk; d += sblock.fs_frag) { blkno = d / sblock.fs_frag; setblock(&sblock, cg_blksfree(&acg), blkno); if (sblock.fs_contigsumsize > 0) setbit(cg_clustersfree(&acg), blkno); acg.cg_cs.cs_nbfree++; } if (d < acg.cg_ndblk) { acg.cg_frsum[acg.cg_ndblk - d]++; for (; d < acg.cg_ndblk; d++) { setbit(cg_blksfree(&acg), d); acg.cg_cs.cs_nffree++; } } if (sblock.fs_contigsumsize > 0) { int32_t *sump = cg_clustersum(&acg); u_char *mapp = cg_clustersfree(&acg); 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_cstotal.cs_ndir += acg.cg_cs.cs_ndir; sblock.fs_cstotal.cs_nffree += acg.cg_cs.cs_nffree; sblock.fs_cstotal.cs_nbfree += acg.cg_cs.cs_nbfree; sblock.fs_cstotal.cs_nifree += acg.cg_cs.cs_nifree; *cs = acg.cg_cs; cgckhash(&acg); wtfs(fsbtodb(&sblock, cgtod(&sblock, cylno)), sblock.fs_cgsize, &acg, fso, Nflag); DBG_DUMP_CG(&sblock, "new cg", &acg); DBG_LEAVE; return; } /* * Here we add or subtract (sign +1/-1) the available fragments in a given * block to or from the fragment statistics. By subtracting before and adding * after an operation on the free frag map we can easy update the fragment * statistic, which seems to be otherwise a rather complex operation. */ static void frag_adjust(ufs2_daddr_t frag, int sign) { DBG_FUNC("frag_adjust") int fragsize; int f; DBG_ENTER; fragsize = 0; /* * Here frag only needs to point to any fragment in the block we want * to examine. */ for (f = rounddown(frag, sblock.fs_frag); f < roundup(frag + 1, sblock.fs_frag); f++) { /* * Count contiguous free fragments. */ if (isset(cg_blksfree(&acg), f)) { fragsize++; } else { if (fragsize && fragsize < sblock.fs_frag) { /* * We found something in between. */ acg.cg_frsum[fragsize] += sign; DBG_PRINT2("frag_adjust [%d]+=%d\n", fragsize, sign); } fragsize = 0; } } if (fragsize && fragsize < sblock.fs_frag) { /* * We found something. */ acg.cg_frsum[fragsize] += sign; DBG_PRINT2("frag_adjust [%d]+=%d\n", fragsize, sign); } DBG_PRINT2("frag_adjust [[%d]]+=%d\n", fragsize, sign); DBG_LEAVE; return; } /* * Here we do all needed work for the former last cylinder group. It has to be * changed in any case, even if the file system ended exactly on the end of * this group, as there is some slightly inconsistent handling of the number * of cylinders in the cylinder group. We start again by reading the cylinder * group from disk. If the last block was not fully available, we first handle * the missing fragments, then we handle all new full blocks in that file * system and finally we handle the new last fragmented block in the file * system. We again have to handle the fragment statistics rotational layout * tables and cluster summary during all those operations. */ static void updjcg(int cylno, time_t modtime, int fsi, int fso, unsigned int Nflag) { DBG_FUNC("updjcg") ufs2_daddr_t cbase, dmax; struct csum *cs; int i, k; int j = 0; DBG_ENTER; /* * Read the former last (joining) cylinder group from disk, and make * a copy. */ rdfs(fsbtodb(&osblock, cgtod(&osblock, cylno)), (size_t)osblock.fs_cgsize, (void *)&aocg, fsi); DBG_PRINT0("jcg read\n"); DBG_DUMP_CG(&sblock, "old joining cg", &aocg); memcpy((void *)&cgun1, (void *)&cgun2, sizeof(cgun2)); /* * If the cylinder group had already its new final size almost * nothing is to be done ... except: * For some reason the value of cg_ncyl in the last cylinder group has * to be zero instead of fs_cpg. As this is now no longer the last * cylinder group we have to change that value now to fs_cpg. */ if (cgbase(&osblock, cylno + 1) == osblock.fs_size) { if (sblock.fs_magic == FS_UFS1_MAGIC) acg.cg_old_ncyl = sblock.fs_old_cpg; cgckhash(&acg); wtfs(fsbtodb(&sblock, cgtod(&sblock, cylno)), (size_t)sblock.fs_cgsize, (void *)&acg, fso, Nflag); DBG_PRINT0("jcg written\n"); DBG_DUMP_CG(&sblock, "new joining cg", &acg); DBG_LEAVE; return; } /* * Set up some variables needed later. */ cbase = cgbase(&sblock, cylno); dmax = cbase + sblock.fs_fpg; if (dmax > sblock.fs_size) dmax = sblock.fs_size; /* * Set pointer to the cylinder summary for our cylinder group. */ cs = fscs + cylno; /* * Touch the cylinder group, update all fields in the cylinder group as * needed, update the free space in the superblock. */ acg.cg_time = modtime; if ((unsigned)cylno == sblock.fs_ncg - 1) { /* * This is still the last cylinder group. */ if (sblock.fs_magic == FS_UFS1_MAGIC) acg.cg_old_ncyl = sblock.fs_old_ncyl % sblock.fs_old_cpg; } else { acg.cg_old_ncyl = sblock.fs_old_cpg; } DBG_PRINT2("jcg dbg: %d %u", cylno, sblock.fs_ncg); #ifdef FS_DEBUG if (sblock.fs_magic == FS_UFS1_MAGIC) DBG_PRINT2("%d %u", acg.cg_old_ncyl, sblock.fs_old_cpg); #endif DBG_PRINT0("\n"); acg.cg_ndblk = dmax - cbase; sblock.fs_dsize += acg.cg_ndblk - aocg.cg_ndblk; sblock.fs_old_dsize = sblock.fs_dsize; if (sblock.fs_contigsumsize > 0) acg.cg_nclusterblks = acg.cg_ndblk / sblock.fs_frag; /* * Now we have to update the free fragment bitmap for our new free * space. There again we have to handle the fragmentation and also * the rotational layout tables and the cluster summary. This is * also done per fragment for the first new block if the old file * system end was not on a block boundary, per fragment for the new * last block if the new file system end is not on a block boundary, * and per block for all space in between. * * Handle the first new block here if it was partially available * before. */ if (osblock.fs_size % sblock.fs_frag) { if (roundup(osblock.fs_size, sblock.fs_frag) <= sblock.fs_size) { /* * The new space is enough to fill at least this * block */ j = 0; for (i = roundup(osblock.fs_size - cbase, sblock.fs_frag) - 1; i >= osblock.fs_size - cbase; i--) { setbit(cg_blksfree(&acg), i); acg.cg_cs.cs_nffree++; j++; } /* * Check if the fragment just created could join an * already existing fragment at the former end of the * file system. */ if (isblock(&sblock, cg_blksfree(&acg), ((osblock.fs_size - cgbase(&sblock, cylno)) / sblock.fs_frag))) { /* * The block is now completely available. */ DBG_PRINT0("block was\n"); acg.cg_frsum[osblock.fs_size % sblock.fs_frag]--; acg.cg_cs.cs_nbfree++; acg.cg_cs.cs_nffree -= sblock.fs_frag; k = rounddown(osblock.fs_size - cbase, sblock.fs_frag); updclst((osblock.fs_size - cbase) / sblock.fs_frag); } else { /* * Lets rejoin a possible partially growed * fragment. */ k = 0; while (isset(cg_blksfree(&acg), i) && (i >= rounddown(osblock.fs_size - cbase, sblock.fs_frag))) { i--; k++; } if (k) acg.cg_frsum[k]--; acg.cg_frsum[k + j]++; } } else { /* * We only grow by some fragments within this last * block. */ for (i = sblock.fs_size - cbase - 1; i >= osblock.fs_size - cbase; i--) { setbit(cg_blksfree(&acg), i); acg.cg_cs.cs_nffree++; j++; } /* * Lets rejoin a possible partially growed fragment. */ k = 0; while (isset(cg_blksfree(&acg), i) && (i >= rounddown(osblock.fs_size - cbase, sblock.fs_frag))) { i--; k++; } if (k) acg.cg_frsum[k]--; acg.cg_frsum[k + j]++; } } /* * Handle all new complete blocks here. */ for (i = roundup(osblock.fs_size - cbase, sblock.fs_frag); i + sblock.fs_frag <= dmax - cbase; /* XXX <= or only < ? */ i += sblock.fs_frag) { j = i / sblock.fs_frag; setblock(&sblock, cg_blksfree(&acg), j); updclst(j); acg.cg_cs.cs_nbfree++; } /* * Handle the last new block if there are stll some new fragments left. * Here we don't have to bother about the cluster summary or the even * the rotational layout table. */ if (i < (dmax - cbase)) { acg.cg_frsum[dmax - cbase - i]++; for (; i < dmax - cbase; i++) { setbit(cg_blksfree(&acg), i); acg.cg_cs.cs_nffree++; } } sblock.fs_cstotal.cs_nffree += (acg.cg_cs.cs_nffree - aocg.cg_cs.cs_nffree); sblock.fs_cstotal.cs_nbfree += (acg.cg_cs.cs_nbfree - aocg.cg_cs.cs_nbfree); /* * The following statistics are not changed here: * sblock.fs_cstotal.cs_ndir * sblock.fs_cstotal.cs_nifree * As the statistics for this cylinder group are ready, copy it to * the summary information array. */ *cs = acg.cg_cs; /* * Write the updated "joining" cylinder group back to disk. */ cgckhash(&acg); wtfs(fsbtodb(&sblock, cgtod(&sblock, cylno)), (size_t)sblock.fs_cgsize, (void *)&acg, fso, Nflag); DBG_PRINT0("jcg written\n"); DBG_DUMP_CG(&sblock, "new joining cg", &acg); DBG_LEAVE; return; } /* * Here we update the location of the cylinder summary. We have two possible * ways of growing the cylinder summary: * (1) We can try to grow the summary in the current location, and relocate * possibly used blocks within the current cylinder group. * (2) Alternatively we can relocate the whole cylinder summary to the first * new completely empty cylinder group. Once the cylinder summary is no * longer in the beginning of the first cylinder group you should never * use a version of fsck which is not aware of the possibility to have * this structure in a non standard place. * Option (2) is considered to be less intrusive to the structure of the file- * system, so that's the one being used. */ static void updcsloc(time_t modtime, int fsi, int fso, unsigned int Nflag) { DBG_FUNC("updcsloc") struct csum *cs; int ocscg, ncscg; ufs2_daddr_t d; int lcs = 0; int block; DBG_ENTER; if (howmany(sblock.fs_cssize, sblock.fs_fsize) == howmany(osblock.fs_cssize, osblock.fs_fsize)) { /* * No new fragment needed. */ DBG_LEAVE; return; } /* Adjust fs_dsize by added summary blocks */ sblock.fs_dsize -= howmany(sblock.fs_cssize, sblock.fs_fsize) - howmany(osblock.fs_cssize, osblock.fs_fsize); sblock.fs_old_dsize = sblock.fs_dsize; ocscg = dtog(&osblock, osblock.fs_csaddr); cs = fscs + ocscg; /* * Read original cylinder group from disk, and make a copy. * XXX If Nflag is set in some very rare cases we now miss * some changes done in updjcg by reading the unmodified * block from disk. */ rdfs(fsbtodb(&osblock, cgtod(&osblock, ocscg)), (size_t)osblock.fs_cgsize, (void *)&aocg, fsi); DBG_PRINT0("oscg read\n"); DBG_DUMP_CG(&sblock, "old summary cg", &aocg); memcpy((void *)&cgun1, (void *)&cgun2, sizeof(cgun2)); /* * Touch the cylinder group, set up local variables needed later * and update the superblock. */ acg.cg_time = modtime; /* * XXX In the case of having active snapshots we may need much more * blocks for the copy on write. We need each block twice, and * also up to 8*3 blocks for indirect blocks for all possible * references. */ /* * There is not enough space in the old cylinder group to * relocate all blocks as needed, so we relocate the whole * cylinder group summary to a new group. We try to use the * first complete new cylinder group just created. Within the * cylinder group we align the area immediately after the * cylinder group information location in order to be as * close as possible to the original implementation of ffs. * * First we have to make sure we'll find enough space in the * new cylinder group. If not, then we currently give up. * We start with freeing everything which was used by the * fragments of the old cylinder summary in the current group. * Now we write back the group meta data, read in the needed * meta data from the new cylinder group, and start allocating * within that group. Here we can assume, the group to be * completely empty. Which makes the handling of fragments and * clusters a lot easier. */ DBG_TRC; if (sblock.fs_ncg - osblock.fs_ncg < 2) errx(2, "panic: not enough space"); /* * Point "d" to the first fragment not used by the cylinder * summary. */ d = osblock.fs_csaddr + (osblock.fs_cssize / osblock.fs_fsize); /* * Set up last cluster size ("lcs") already here. Calculate * the size for the trailing cluster just behind where "d" * points to. */ if (sblock.fs_contigsumsize > 0) { for (block = howmany(d % sblock.fs_fpg, sblock.fs_frag), lcs = 0; lcs < sblock.fs_contigsumsize; block++, lcs++) { if (isclr(cg_clustersfree(&acg), block)) break; } } /* * Point "d" to the last frag used by the cylinder summary. */ d--; DBG_PRINT1("d=%jd\n", (intmax_t)d); if ((d + 1) % sblock.fs_frag) { /* * The end of the cylinder summary is not a complete * block. */ DBG_TRC; frag_adjust(d % sblock.fs_fpg, -1); for (; (d + 1) % sblock.fs_frag; d--) { DBG_PRINT1("d=%jd\n", (intmax_t)d); setbit(cg_blksfree(&acg), d % sblock.fs_fpg); acg.cg_cs.cs_nffree++; sblock.fs_cstotal.cs_nffree++; } /* * Point "d" to the last fragment of the last * (incomplete) block of the cylinder summary. */ d++; frag_adjust(d % sblock.fs_fpg, 1); if (isblock(&sblock, cg_blksfree(&acg), (d % sblock.fs_fpg) / sblock.fs_frag)) { DBG_PRINT1("d=%jd\n", (intmax_t)d); acg.cg_cs.cs_nffree -= sblock.fs_frag; acg.cg_cs.cs_nbfree++; sblock.fs_cstotal.cs_nffree -= sblock.fs_frag; sblock.fs_cstotal.cs_nbfree++; if (sblock.fs_contigsumsize > 0) { setbit(cg_clustersfree(&acg), (d % sblock.fs_fpg) / sblock.fs_frag); if (lcs < sblock.fs_contigsumsize) { if (lcs) cg_clustersum(&acg)[lcs]--; lcs++; cg_clustersum(&acg)[lcs]++; } } } /* * Point "d" to the first fragment of the block before * the last incomplete block. */ d--; } DBG_PRINT1("d=%jd\n", (intmax_t)d); for (d = rounddown(d, sblock.fs_frag); d >= osblock.fs_csaddr; d -= sblock.fs_frag) { DBG_TRC; DBG_PRINT1("d=%jd\n", (intmax_t)d); setblock(&sblock, cg_blksfree(&acg), (d % sblock.fs_fpg) / sblock.fs_frag); acg.cg_cs.cs_nbfree++; sblock.fs_cstotal.cs_nbfree++; if (sblock.fs_contigsumsize > 0) { setbit(cg_clustersfree(&acg), (d % sblock.fs_fpg) / sblock.fs_frag); /* * The last cluster size is already set up. */ if (lcs < sblock.fs_contigsumsize) { if (lcs) cg_clustersum(&acg)[lcs]--; lcs++; cg_clustersum(&acg)[lcs]++; } } } *cs = acg.cg_cs; /* * Now write the former cylinder group containing the cylinder * summary back to disk. */ cgckhash(&acg); wtfs(fsbtodb(&sblock, cgtod(&sblock, ocscg)), (size_t)sblock.fs_cgsize, (void *)&acg, fso, Nflag); DBG_PRINT0("oscg written\n"); DBG_DUMP_CG(&sblock, "old summary cg", &acg); /* * Find the beginning of the new cylinder group containing the * cylinder summary. */ sblock.fs_csaddr = cgdmin(&sblock, osblock.fs_ncg); ncscg = dtog(&sblock, sblock.fs_csaddr); cs = fscs + ncscg; /* * If Nflag is specified, we would now read random data instead * of an empty cg structure from disk. So we can't simulate that * part for now. */ if (Nflag) { DBG_PRINT0("nscg update skipped\n"); DBG_LEAVE; return; } /* * Read the future cylinder group containing the cylinder * summary from disk, and make a copy. */ rdfs(fsbtodb(&sblock, cgtod(&sblock, ncscg)), (size_t)sblock.fs_cgsize, (void *)&aocg, fsi); DBG_PRINT0("nscg read\n"); DBG_DUMP_CG(&sblock, "new summary cg", &aocg); memcpy((void *)&cgun1, (void *)&cgun2, sizeof(cgun2)); /* * Allocate all complete blocks used by the new cylinder * summary. */ for (d = sblock.fs_csaddr; d + sblock.fs_frag <= sblock.fs_csaddr + (sblock.fs_cssize / sblock.fs_fsize); d += sblock.fs_frag) { clrblock(&sblock, cg_blksfree(&acg), (d % sblock.fs_fpg) / sblock.fs_frag); acg.cg_cs.cs_nbfree--; sblock.fs_cstotal.cs_nbfree--; if (sblock.fs_contigsumsize > 0) { clrbit(cg_clustersfree(&acg), (d % sblock.fs_fpg) / sblock.fs_frag); } } /* * Allocate all fragments used by the cylinder summary in the * last block. */ if (d < sblock.fs_csaddr + (sblock.fs_cssize / sblock.fs_fsize)) { for (; d - sblock.fs_csaddr < sblock.fs_cssize/sblock.fs_fsize; d++) { clrbit(cg_blksfree(&acg), d % sblock.fs_fpg); acg.cg_cs.cs_nffree--; sblock.fs_cstotal.cs_nffree--; } acg.cg_cs.cs_nbfree--; acg.cg_cs.cs_nffree += sblock.fs_frag; sblock.fs_cstotal.cs_nbfree--; sblock.fs_cstotal.cs_nffree += sblock.fs_frag; if (sblock.fs_contigsumsize > 0) clrbit(cg_clustersfree(&acg), (d % sblock.fs_fpg) / sblock.fs_frag); frag_adjust(d % sblock.fs_fpg, 1); } /* * XXX Handle the cluster statistics here in the case this * cylinder group is now almost full, and the remaining * space is less then the maximum cluster size. This is * probably not needed, as you would hardly find a file * system which has only MAXCSBUFS+FS_MAXCONTIG of free * space right behind the cylinder group information in * any new cylinder group. */ /* * Update our statistics in the cylinder summary. */ *cs = acg.cg_cs; /* * Write the new cylinder group containing the cylinder summary * back to disk. */ cgckhash(&acg); wtfs(fsbtodb(&sblock, cgtod(&sblock, ncscg)), (size_t)sblock.fs_cgsize, (void *)&acg, fso, Nflag); DBG_PRINT0("nscg written\n"); DBG_DUMP_CG(&sblock, "new summary cg", &acg); DBG_LEAVE; return; } /* * Here we read some block(s) from disk. */ static void rdfs(ufs2_daddr_t bno, size_t size, void *bf, int fsi) { DBG_FUNC("rdfs") ssize_t n; DBG_ENTER; if (bno < 0) err(32, "rdfs: attempting to read negative block number"); if (lseek(fsi, (off_t)bno * DEV_BSIZE, 0) < 0) err(33, "rdfs: seek error: %jd", (intmax_t)bno); n = read(fsi, bf, size); if (n != (ssize_t)size) err(34, "rdfs: read error: %jd", (intmax_t)bno); DBG_LEAVE; return; } /* * Here we write some block(s) to disk. */ static void wtfs(ufs2_daddr_t bno, size_t size, void *bf, int fso, unsigned int Nflag) { DBG_FUNC("wtfs") ssize_t n; DBG_ENTER; if (Nflag) { DBG_LEAVE; return; } if (lseek(fso, (off_t)bno * DEV_BSIZE, SEEK_SET) < 0) err(35, "wtfs: seek error: %ld", (long)bno); n = write(fso, bf, size); if (n != (ssize_t)size) err(36, "wtfs: write error: %ld", (long)bno); DBG_LEAVE; return; } /* * Here we check if all frags of a block are free. For more details again * please see the source of newfs(8), as this function is taken over almost * unchanged. */ static int isblock(struct fs *fs, unsigned char *cp, int h) { DBG_FUNC("isblock") unsigned char mask; DBG_ENTER; switch (fs->fs_frag) { case 8: DBG_LEAVE; return (cp[h] == 0xff); case 4: mask = 0x0f << ((h & 0x1) << 2); DBG_LEAVE; return ((cp[h >> 1] & mask) == mask); case 2: mask = 0x03 << ((h & 0x3) << 1); DBG_LEAVE; return ((cp[h >> 2] & mask) == mask); case 1: mask = 0x01 << (h & 0x7); DBG_LEAVE; return ((cp[h >> 3] & mask) == mask); default: fprintf(stderr, "isblock bad fs_frag %d\n", fs->fs_frag); DBG_LEAVE; return (0); } } /* * Here we allocate a complete block in the block map. For more details again * please see the source of newfs(8), as this function is taken over almost * unchanged. */ static void clrblock(struct fs *fs, unsigned char *cp, int h) { DBG_FUNC("clrblock") DBG_ENTER; switch ((fs)->fs_frag) { case 8: cp[h] = 0; break; case 4: cp[h >> 1] &= ~(0x0f << ((h & 0x1) << 2)); break; case 2: cp[h >> 2] &= ~(0x03 << ((h & 0x3) << 1)); break; case 1: cp[h >> 3] &= ~(0x01 << (h & 0x7)); break; default: warnx("clrblock bad fs_frag %d", fs->fs_frag); break; } DBG_LEAVE; return; } /* * Here we free a complete block in the free block map. For more details again * please see the source of newfs(8), as this function is taken over almost * unchanged. */ static void setblock(struct fs *fs, unsigned char *cp, int h) { DBG_FUNC("setblock") DBG_ENTER; switch (fs->fs_frag) { case 8: cp[h] = 0xff; break; case 4: cp[h >> 1] |= (0x0f << ((h & 0x1) << 2)); break; case 2: cp[h >> 2] |= (0x03 << ((h & 0x3) << 1)); break; case 1: cp[h >> 3] |= (0x01 << (h & 0x7)); break; default: warnx("setblock bad fs_frag %d", fs->fs_frag); break; } DBG_LEAVE; return; } /* * Figure out how many lines our current terminal has. For more details again * please see the source of newfs(8), as this function is taken over almost * unchanged. */ static int charsperline(void) { DBG_FUNC("charsperline") int columns; char *cp; struct winsize ws; DBG_ENTER; columns = 0; if (ioctl(0, TIOCGWINSZ, &ws) != -1) columns = ws.ws_col; if (columns == 0 && (cp = getenv("COLUMNS"))) columns = atoi(cp); if (columns == 0) columns = 80; /* last resort */ DBG_LEAVE; return (columns); } static int is_dev(const char *name) { struct stat devstat; if (stat(name, &devstat) != 0) return (0); if (!S_ISCHR(devstat.st_mode)) return (0); return (1); } /* * Return mountpoint on which the device is currently mounted. */ static const struct statfs * dev_to_statfs(const char *dev) { struct stat devstat, mntdevstat; struct statfs *mntbuf, *statfsp; char device[MAXPATHLEN]; char *mntdevname; int i, mntsize; /* * First check the mounted filesystems. */ if (stat(dev, &devstat) != 0) return (NULL); if (!S_ISCHR(devstat.st_mode) && !S_ISBLK(devstat.st_mode)) return (NULL); mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); for (i = 0; i < mntsize; i++) { statfsp = &mntbuf[i]; mntdevname = statfsp->f_mntfromname; if (*mntdevname != '/') { strcpy(device, _PATH_DEV); strcat(device, mntdevname); mntdevname = device; } if (stat(mntdevname, &mntdevstat) == 0 && mntdevstat.st_rdev == devstat.st_rdev) return (statfsp); } return (NULL); } static const char * mountpoint_to_dev(const char *mountpoint) { struct statfs *mntbuf, *statfsp; struct fstab *fs; int i, mntsize; /* * First check the mounted filesystems. */ mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); for (i = 0; i < mntsize; i++) { statfsp = &mntbuf[i]; if (strcmp(statfsp->f_mntonname, mountpoint) == 0) return (statfsp->f_mntfromname); } /* * Check the fstab. */ fs = getfsfile(mountpoint); if (fs != NULL) return (fs->fs_spec); return (NULL); } static const char * getdev(const char *name) { static char device[MAXPATHLEN]; const char *cp, *dev; if (is_dev(name)) return (name); cp = strrchr(name, '/'); if (cp == NULL) { snprintf(device, sizeof(device), "%s%s", _PATH_DEV, name); if (is_dev(device)) return (device); } dev = mountpoint_to_dev(name); if (dev != NULL && is_dev(dev)) return (dev); return (NULL); } /* * growfs(8) is a utility which allows to increase the size of an existing * ufs file system. Currently this can only be done on unmounted file system. * It recognizes some command line options to specify the new desired size, * and it does some basic checkings. The old file system size is determined * and after some more checks like we can really access the new last block * on the disk etc. we calculate the new parameters for the superblock. After * having done this we just call growfs() which will do the work. * We still have to provide support for snapshots. Therefore we first have to * understand what data structures are always replicated in the snapshot on * creation, for all other blocks we touch during our procedure, we have to * keep the old blocks unchanged somewhere available for the snapshots. If we * are lucky, then we only have to handle our blocks to be relocated in that * way. * Also we have to consider in what order we actually update the critical * data structures of the file system to make sure, that in case of a disaster * fsck(8) is still able to restore any lost data. * The foreseen last step then will be to provide for growing even mounted * file systems. There we have to extend the mount() system call to provide * userland access to the file system locking facility. */ int main(int argc, char **argv) { DBG_FUNC("main") struct fs *fs; const char *device; const struct statfs *statfsp; uint64_t size = 0; off_t mediasize; int error, j, fsi, fso, ch, ret, Nflag = 0, yflag = 0; char *p, reply[5], oldsizebuf[6], newsizebuf[6]; void *testbuf; DBG_ENTER; while ((ch = getopt(argc, argv, "Ns:vy")) != -1) { switch(ch) { case 'N': Nflag = 1; break; case 's': size = (off_t)strtoumax(optarg, &p, 0); if (p == NULL || *p == '\0') size *= DEV_BSIZE; else if (*p == 'b' || *p == 'B') ; /* do nothing */ else if (*p == 'k' || *p == 'K') size <<= 10; else if (*p == 'm' || *p == 'M') size <<= 20; else if (*p == 'g' || *p == 'G') size <<= 30; else if (*p == 't' || *p == 'T') { size <<= 30; size <<= 10; } else errx(1, "unknown suffix on -s argument"); break; case 'v': /* for compatibility to newfs */ break; case 'y': yflag = 1; break; case '?': /* FALLTHROUGH */ default: usage(); } } argc -= optind; argv += optind; if (argc != 1) usage(); /* * Now try to guess the device name. */ device = getdev(*argv); if (device == NULL) errx(1, "cannot find special device for %s", *argv); statfsp = dev_to_statfs(device); fsi = open(device, O_RDONLY); if (fsi < 0) err(1, "%s", device); /* * Try to guess the slice size if not specified. */ if (ioctl(fsi, DIOCGMEDIASIZE, &mediasize) == -1) err(1,"DIOCGMEDIASIZE"); /* * Check if that partition is suitable for growing a file system. */ if (mediasize < 1) errx(1, "partition is unavailable"); /* * Read the current superblock, and take a backup. */ - if ((ret = sbget(fsi, &fs, STDSB)) != 0) { + if ((ret = sbget(fsi, &fs, UFS_STDSB, 0)) != 0) { switch (ret) { case ENOENT: errx(1, "superblock not recognized"); default: errc(1, ret, "unable to read superblock"); } } /* * Check for filesystem that was unclean at mount time. */ if ((fs->fs_flags & (FS_UNCLEAN | FS_NEEDSFSCK)) != 0) errx(1, "%s is not clean - run fsck.\n", *argv); memcpy(&osblock, fs, fs->fs_sbsize); free(fs); memcpy((void *)&fsun1, (void *)&fsun2, osblock.fs_sbsize); DBG_OPEN("/tmp/growfs.debug"); /* already here we need a superblock */ DBG_DUMP_FS(&sblock, "old sblock"); /* * Determine size to grow to. Default to the device size. */ if (size == 0) size = mediasize; else { if (size > (uint64_t)mediasize) { humanize_number(oldsizebuf, sizeof(oldsizebuf), size, "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); humanize_number(newsizebuf, sizeof(newsizebuf), mediasize, "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); errx(1, "requested size %s is larger " "than the available %s", oldsizebuf, newsizebuf); } } /* * Make sure the new size is a multiple of fs_fsize; /dev/ufssuspend * only supports fragment-aligned IO requests. */ size -= size % osblock.fs_fsize; if (size <= (uint64_t)(osblock.fs_size * osblock.fs_fsize)) { humanize_number(oldsizebuf, sizeof(oldsizebuf), osblock.fs_size * osblock.fs_fsize, "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); humanize_number(newsizebuf, sizeof(newsizebuf), size, "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); if (size == (uint64_t)(osblock.fs_size * osblock.fs_fsize)) errx(0, "requested size %s is equal to the current " "filesystem size %s", newsizebuf, oldsizebuf); errx(1, "requested size %s is smaller than the current " "filesystem size %s", newsizebuf, oldsizebuf); } sblock.fs_old_size = sblock.fs_size = dbtofsb(&osblock, size / DEV_BSIZE); sblock.fs_providersize = dbtofsb(&osblock, mediasize / DEV_BSIZE); /* * Are we really growing? */ if (osblock.fs_size >= sblock.fs_size) { errx(1, "we are not growing (%jd->%jd)", (intmax_t)osblock.fs_size, (intmax_t)sblock.fs_size); } /* * Check if we find an active snapshot. */ if (yflag == 0) { for (j = 0; j < FSMAXSNAP; j++) { if (sblock.fs_snapinum[j]) { errx(1, "active snapshot found in file system; " "please remove all snapshots before " "using growfs"); } if (!sblock.fs_snapinum[j]) /* list is dense */ break; } } if (yflag == 0 && Nflag == 0) { if (statfsp != NULL && (statfsp->f_flags & MNT_RDONLY) == 0) printf("Device is mounted read-write; resizing will " "result in temporary write suspension for %s.\n", statfsp->f_mntonname); printf("It's strongly recommended to make a backup " "before growing the file system.\n" "OK to grow filesystem on %s", device); if (statfsp != NULL) printf(", mounted on %s,", statfsp->f_mntonname); humanize_number(oldsizebuf, sizeof(oldsizebuf), osblock.fs_size * osblock.fs_fsize, "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); humanize_number(newsizebuf, sizeof(newsizebuf), sblock.fs_size * sblock.fs_fsize, "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); printf(" from %s to %s? [yes/no] ", oldsizebuf, newsizebuf); fflush(stdout); fgets(reply, (int)sizeof(reply), stdin); if (strcasecmp(reply, "yes\n")){ printf("Response other than \"yes\"; aborting\n"); exit(0); } } /* * Try to access our device for writing. If it's not mounted, * or mounted read-only, simply open it; otherwise, use UFS * suspension mechanism. */ if (Nflag) { fso = -1; } else { if (statfsp != NULL && (statfsp->f_flags & MNT_RDONLY) == 0) { fso = open(_PATH_UFSSUSPEND, O_RDWR); if (fso == -1) err(1, "unable to open %s", _PATH_UFSSUSPEND); error = ioctl(fso, UFSSUSPEND, &statfsp->f_fsid); if (error != 0) err(1, "UFSSUSPEND"); } else { fso = open(device, O_WRONLY); if (fso < 0) err(1, "%s", device); } } /* * Try to access our new last block in the file system. */ testbuf = malloc(sblock.fs_fsize); if (testbuf == NULL) err(1, "malloc"); rdfs((ufs2_daddr_t)((size - sblock.fs_fsize) / DEV_BSIZE), sblock.fs_fsize, testbuf, fsi); wtfs((ufs2_daddr_t)((size - sblock.fs_fsize) / DEV_BSIZE), sblock.fs_fsize, testbuf, fso, Nflag); free(testbuf); /* * Now calculate new superblock values and check for reasonable * bound for new file system size: * fs_size: is derived from user input * fs_dsize: should get updated in the routines creating or * updating the cylinder groups on the fly * fs_cstotal: should get updated in the routines creating or * updating the cylinder groups */ /* * Update the number of cylinders and cylinder groups in the file system. */ if (sblock.fs_magic == FS_UFS1_MAGIC) { sblock.fs_old_ncyl = sblock.fs_size * sblock.fs_old_nspf / sblock.fs_old_spc; if (sblock.fs_size * sblock.fs_old_nspf > sblock.fs_old_ncyl * sblock.fs_old_spc) sblock.fs_old_ncyl++; } sblock.fs_ncg = howmany(sblock.fs_size, sblock.fs_fpg); /* * Allocate last cylinder group only if there is enough room * for at least one data block. */ if (sblock.fs_size % sblock.fs_fpg != 0 && sblock.fs_size <= cgdmin(&sblock, sblock.fs_ncg - 1)) { humanize_number(oldsizebuf, sizeof(oldsizebuf), (sblock.fs_size % sblock.fs_fpg) * sblock.fs_fsize, "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); warnx("no room to allocate last cylinder group; " "leaving %s unused", oldsizebuf); sblock.fs_ncg--; if (sblock.fs_magic == FS_UFS1_MAGIC) sblock.fs_old_ncyl = sblock.fs_ncg * sblock.fs_old_cpg; sblock.fs_old_size = sblock.fs_size = sblock.fs_ncg * sblock.fs_fpg; } /* * Update the space for the cylinder group summary information in the * respective cylinder group data area. */ sblock.fs_cssize = fragroundup(&sblock, sblock.fs_ncg * sizeof(struct csum)); if (osblock.fs_size >= sblock.fs_size) errx(1, "not enough new space"); DBG_PRINT0("sblock calculated\n"); /* * Ok, everything prepared, so now let's do the tricks. */ growfs(fsi, fso, Nflag); close(fsi); if (fso > -1) { if (statfsp != NULL && (statfsp->f_flags & MNT_RDONLY) == 0) { error = ioctl(fso, UFSRESUME); if (error != 0) err(1, "UFSRESUME"); } error = close(fso); if (error != 0) err(1, "close"); if (statfsp != NULL && (statfsp->f_flags & MNT_RDONLY) != 0) mount_reload(statfsp); } DBG_CLOSE; DBG_LEAVE; return (0); } /* * Dump a line of usage. */ static void usage(void) { DBG_FUNC("usage") DBG_ENTER; fprintf(stderr, "usage: growfs [-Ny] [-s size] special | filesystem\n"); DBG_LEAVE; exit(1); } /* * This updates most parameters and the bitmap related to cluster. We have to * assume that sblock, osblock, acg are set up. */ static void updclst(int block) { DBG_FUNC("updclst") static int lcs = 0; DBG_ENTER; if (sblock.fs_contigsumsize < 1) /* no clustering */ return; /* * update cluster allocation map */ setbit(cg_clustersfree(&acg), block); /* * update cluster summary table */ if (!lcs) { /* * calculate size for the trailing cluster */ for (block--; lcs < sblock.fs_contigsumsize; block--, lcs++ ) { if (isclr(cg_clustersfree(&acg), block)) break; } } if (lcs < sblock.fs_contigsumsize) { if (lcs) cg_clustersum(&acg)[lcs]--; lcs++; cg_clustersum(&acg)[lcs]++; } DBG_LEAVE; return; } static void mount_reload(const struct statfs *stfs) { char errmsg[255]; struct iovec *iov; int iovlen; iov = NULL; iovlen = 0; *errmsg = '\0'; build_iovec(&iov, &iovlen, "fstype", __DECONST(char *, "ffs"), 4); build_iovec(&iov, &iovlen, "fspath", __DECONST(char *, stfs->f_mntonname), (size_t)-1); build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); build_iovec(&iov, &iovlen, "update", NULL, 0); build_iovec(&iov, &iovlen, "reload", NULL, 0); if (nmount(iov, iovlen, stfs->f_flags) < 0) { errmsg[sizeof(errmsg) - 1] = '\0'; err(9, "%s: cannot reload filesystem%s%s", stfs->f_mntonname, *errmsg != '\0' ? ": " : "", errmsg); } } /* * Calculate the check-hash of the cylinder group. */ static void cgckhash(struct cg *cgp) { if ((sblock.fs_metackhash & CK_CYLGRP) == 0) return; cgp->cg_ckhash = 0; cgp->cg_ckhash = calculate_crc32c(~0L, (void *)cgp, sblock.fs_cgsize); } diff --git a/sbin/quotacheck/quotacheck.c b/sbin/quotacheck/quotacheck.c index 9a01be11d9d0..1e656d2a5811 100644 --- a/sbin/quotacheck/quotacheck.c +++ b/sbin/quotacheck/quotacheck.c @@ -1,724 +1,724 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1990, 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. */ #if 0 #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1980, 1990, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint static char sccsid[] = "@(#)quotacheck.c 8.3 (Berkeley) 1/29/94"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); /* * Fix up / report on disk quotas & usage */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "quotacheck.h" const char *qfname = QUOTAFILENAME; const char *qfextension[] = INITQFNAMES; const char *quotagroup = QUOTAGROUP; union { struct fs sblk; char dummy[MAXBSIZE]; } sb_un; #define sblock sb_un.sblk union { struct cg cgblk; char dummy[MAXBSIZE]; } cg_un; #define cgblk cg_un.cgblk long dev_bsize = 1; ino_t maxino; union dinode { struct ufs1_dinode dp1; struct ufs2_dinode dp2; }; #define DIP(dp, field) \ ((sblock.fs_magic == FS_UFS1_MAGIC) ? \ (dp)->dp1.field : (dp)->dp2.field) #define HASUSR 1 #define HASGRP 2 struct fileusage { struct fileusage *fu_next; u_long fu_curinodes; u_long fu_curblocks; u_long fu_id; char fu_name[1]; /* actually bigger */ }; #define FUHASH 1024 /* must be power of two */ struct fileusage *fuhead[MAXQUOTAS][FUHASH]; int aflag; /* all file systems */ int cflag; /* convert format to 32 or 64 bit size */ int gflag; /* check group quotas */ int uflag; /* check user quotas */ int vflag; /* verbose */ int fi; /* open disk file descriptor */ struct fileusage * addid(u_long, int, char *, const char *); void blkread(ufs2_daddr_t, char *, long); void freeinodebuf(void); union dinode * getnextinode(ino_t); int getquotagid(void); struct fileusage * lookup(u_long, int); int oneof(char *, char*[], int); void printchanges(const char *, int, struct dqblk *, struct fileusage *, u_long); void setinodebuf(ino_t); int update(const char *, struct quotafile *, int); void usage(void); int main(int argc, char *argv[]) { struct fstab *fs; struct passwd *pw; struct group *gr; struct quotafile *qfu, *qfg; int i, argnum, maxrun, errs, ch; long done = 0; char *name; errs = maxrun = 0; while ((ch = getopt(argc, argv, "ac:guvl:")) != -1) { switch(ch) { case 'a': aflag++; break; case 'c': if (cflag) usage(); cflag = atoi(optarg); break; case 'g': gflag++; break; case 'u': uflag++; break; case 'v': vflag++; break; case 'l': maxrun = atoi(optarg); break; default: usage(); } } argc -= optind; argv += optind; if ((argc == 0 && !aflag) || (argc > 0 && aflag)) usage(); if (cflag && cflag != 32 && cflag != 64) usage(); if (!gflag && !uflag) { gflag++; uflag++; } if (gflag) { setgrent(); while ((gr = getgrent()) != NULL) (void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name, NULL); endgrent(); } if (uflag) { setpwent(); while ((pw = getpwent()) != NULL) (void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name, NULL); endpwent(); } /* * The maxrun (-l) option is now deprecated. */ if (maxrun > 0) warnx("the -l option is now deprecated"); if (aflag) exit(checkfstab(uflag, gflag)); if (setfsent() == 0) errx(1, "%s: can't open", FSTAB); while ((fs = getfsent()) != NULL) { if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 || (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) && (name = blockcheck(fs->fs_spec))) { done |= 1 << argnum; qfu = NULL; if (uflag) qfu = quota_open(fs, USRQUOTA, O_CREAT|O_RDWR); qfg = NULL; if (gflag) qfg = quota_open(fs, GRPQUOTA, O_CREAT|O_RDWR); if (qfu == NULL && qfg == NULL) continue; errs += chkquota(name, qfu, qfg); if (qfu) quota_close(qfu); if (qfg) quota_close(qfg); } } endfsent(); for (i = 0; i < argc; i++) if ((done & (1 << i)) == 0) fprintf(stderr, "%s not found in %s\n", argv[i], FSTAB); exit(errs); } void usage(void) { (void)fprintf(stderr, "%s\n%s\n", "usage: quotacheck [-guv] [-c 32 | 64] [-l maxrun] -a", " quotacheck [-guv] [-c 32 | 64] filesystem ..."); exit(1); } /* * Scan the specified file system to check quota(s) present on it. */ int chkquota(char *specname, struct quotafile *qfu, struct quotafile *qfg) { struct fileusage *fup; union dinode *dp; struct fs *fs; int i, ret, mode, errs = 0; u_int32_t cg; ino_t curino, ino, inosused, userino = 0, groupino = 0; dev_t dev, userdev = 0, groupdev = 0; struct stat sb; const char *mntpt; char *cp; if (qfu != NULL) mntpt = quota_fsname(qfu); else if (qfg != NULL) mntpt = quota_fsname(qfg); else errx(1, "null quotafile information passed to chkquota()\n"); if (cflag) { if (vflag && qfu != NULL) printf("%s: convert user quota to %d bits\n", mntpt, cflag); if (qfu != NULL && quota_convert(qfu, cflag) < 0) { if (errno == EBADF) errx(1, "%s: cannot convert an active quota file", mntpt); err(1, "user quota conversion to size %d failed", cflag); } if (vflag && qfg != NULL) printf("%s: convert group quota to %d bits\n", mntpt, cflag); if (qfg != NULL && quota_convert(qfg, cflag) < 0) { if (errno == EBADF) errx(1, "%s: cannot convert an active quota file", mntpt); err(1, "group quota conversion to size %d failed", cflag); } } if ((fi = open(specname, O_RDONLY, 0)) < 0) { warn("%s", specname); return (1); } if ((stat(mntpt, &sb)) < 0) { warn("%s", mntpt); return (1); } dev = sb.st_dev; if (vflag) { (void)printf("*** Checking "); if (qfu) (void)printf("user%s", qfg ? " and " : ""); if (qfg) (void)printf("group"); (void)printf(" quotas for %s (%s)\n", specname, mntpt); } if (qfu) { if (stat(quota_qfname(qfu), &sb) == 0) { userino = sb.st_ino; userdev = sb.st_dev; } } if (qfg) { if (stat(quota_qfname(qfg), &sb) == 0) { groupino = sb.st_ino; groupdev = sb.st_dev; } } sync(); - if ((ret = sbget(fi, &fs, STDSB)) != 0) { + if ((ret = sbget(fi, &fs, UFS_STDSB, UFS_NOCSUM)) != 0) { switch (ret) { case ENOENT: warn("Cannot find file system superblock"); return (1); default: warn("Unable to read file system superblock"); return (1); } } bcopy(fs, &sblock, fs->fs_sbsize); free(fs); dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1); maxino = sblock.fs_ncg * sblock.fs_ipg; for (cg = 0; cg < sblock.fs_ncg; cg++) { ino = cg * sblock.fs_ipg; setinodebuf(ino); blkread(fsbtodb(&sblock, cgtod(&sblock, cg)), (char *)(&cgblk), sblock.fs_cgsize); if (sblock.fs_magic == FS_UFS2_MAGIC) inosused = cgblk.cg_initediblk; else inosused = sblock.fs_ipg; /* * If we are using soft updates, then we can trust the * cylinder group inode allocation maps to tell us which * inodes are allocated. We will scan the used inode map * to find the inodes that are really in use, and then * read only those inodes in from disk. */ if (sblock.fs_flags & FS_DOSOFTDEP) { if (!cg_chkmagic(&cgblk)) errx(1, "CG %d: BAD MAGIC NUMBER\n", cg); cp = &cg_inosused(&cgblk)[(inosused - 1) / CHAR_BIT]; for ( ; inosused > 0; inosused -= CHAR_BIT, cp--) { if (*cp == 0) continue; for (i = 1 << (CHAR_BIT - 1); i > 0; i >>= 1) { if (*cp & i) break; inosused--; } break; } if (inosused <= 0) continue; } for (curino = 0; curino < inosused; curino++, ino++) { if ((dp = getnextinode(ino)) == NULL || ino < UFS_ROOTINO || (mode = DIP(dp, di_mode) & IFMT) == 0) continue; /* * XXX: Do not account for UIDs or GIDs that appear * to be negative to prevent generating 100GB+ * quota files. */ if ((int)DIP(dp, di_uid) < 0 || (int)DIP(dp, di_gid) < 0) { if (vflag) { if (aflag) (void)printf("%s: ", mntpt); (void)printf("out of range UID/GID (%u/%u) ino=%ju\n", DIP(dp, di_uid), DIP(dp,di_gid), (uintmax_t)ino); } continue; } /* * Do not account for file system snapshot files * or the actual quota data files to be consistent * with how they are handled inside the kernel. */ #ifdef SF_SNAPSHOT if (DIP(dp, di_flags) & SF_SNAPSHOT) continue; #endif if ((ino == userino && dev == userdev) || (ino == groupino && dev == groupdev)) continue; if (qfg) { fup = addid((u_long)DIP(dp, di_gid), GRPQUOTA, NULL, mntpt); fup->fu_curinodes++; if (mode == IFREG || mode == IFDIR || mode == IFLNK) fup->fu_curblocks += DIP(dp, di_blocks); } if (qfu) { fup = addid((u_long)DIP(dp, di_uid), USRQUOTA, NULL, mntpt); fup->fu_curinodes++; if (mode == IFREG || mode == IFDIR || mode == IFLNK) fup->fu_curblocks += DIP(dp, di_blocks); } } } freeinodebuf(); if (qfu) errs += update(mntpt, qfu, USRQUOTA); if (qfg) errs += update(mntpt, qfg, GRPQUOTA); close(fi); (void)fflush(stdout); return (errs); } /* * Update a specified quota file. */ int update(const char *fsname, struct quotafile *qf, int type) { struct fileusage *fup; u_long id, lastid, highid = 0; struct dqblk dqbuf; struct stat sb; static struct dqblk zerodqbuf; static struct fileusage zerofileusage; /* * Scan the on-disk quota file and record any usage changes. */ lastid = quota_maxid(qf); for (id = 0; id <= lastid; id++) { if (quota_read(qf, &dqbuf, id) < 0) dqbuf = zerodqbuf; if ((fup = lookup(id, type)) == NULL) fup = &zerofileusage; if (fup->fu_curinodes || fup->fu_curblocks || dqbuf.dqb_bsoftlimit || dqbuf.dqb_bhardlimit || dqbuf.dqb_isoftlimit || dqbuf.dqb_ihardlimit) highid = id; if (dqbuf.dqb_curinodes == fup->fu_curinodes && dqbuf.dqb_curblocks == fup->fu_curblocks) { fup->fu_curinodes = 0; fup->fu_curblocks = 0; continue; } printchanges(fsname, type, &dqbuf, fup, id); dqbuf.dqb_curinodes = fup->fu_curinodes; dqbuf.dqb_curblocks = fup->fu_curblocks; (void) quota_write_usage(qf, &dqbuf, id); fup->fu_curinodes = 0; fup->fu_curblocks = 0; } /* * Walk the hash table looking for ids with non-zero usage * that are not currently recorded in the quota file. E.g. * ids that are past the end of the current file. */ for (id = 0; id < FUHASH; id++) { for (fup = fuhead[type][id]; fup != NULL; fup = fup->fu_next) { if (fup->fu_id <= lastid) continue; if (fup->fu_curinodes == 0 && fup->fu_curblocks == 0) continue; bzero(&dqbuf, sizeof(struct dqblk)); if (fup->fu_id > highid) highid = fup->fu_id; printchanges(fsname, type, &dqbuf, fup, fup->fu_id); dqbuf.dqb_curinodes = fup->fu_curinodes; dqbuf.dqb_curblocks = fup->fu_curblocks; (void) quota_write_usage(qf, &dqbuf, fup->fu_id); fup->fu_curinodes = 0; fup->fu_curblocks = 0; } } /* * If this is old format file, then size may be smaller, * so ensure that we only truncate when it will make things * smaller, and not if it will grow an old format file. */ if (highid < lastid && stat(quota_qfname(qf), &sb) == 0 && sb.st_size > (off_t)((highid + 2) * sizeof(struct dqblk))) truncate(quota_qfname(qf), (((off_t)highid + 2) * sizeof(struct dqblk))); return (0); } /* * Check to see if target appears in list of size cnt. */ int oneof(char *target, char *list[], int cnt) { int i; for (i = 0; i < cnt; i++) if (strcmp(target, list[i]) == 0) return (i); return (-1); } /* * Determine the group identifier for quota files. */ int getquotagid(void) { struct group *gr; if ((gr = getgrnam(quotagroup)) != NULL) return (gr->gr_gid); return (-1); } /* * Routines to manage the file usage table. * * Lookup an id of a specific type. */ struct fileusage * lookup(u_long id, int type) { struct fileusage *fup; for (fup = fuhead[type][id & (FUHASH-1)]; fup != NULL; fup = fup->fu_next) if (fup->fu_id == id) return (fup); return (NULL); } /* * Add a new file usage id if it does not already exist. */ struct fileusage * addid(u_long id, int type, char *name, const char *fsname) { struct fileusage *fup, **fhp; int len; if ((fup = lookup(id, type)) != NULL) return (fup); if (name) len = strlen(name); else len = 0; if ((fup = calloc(1, sizeof(*fup) + len)) == NULL) errx(1, "calloc failed"); fhp = &fuhead[type][id & (FUHASH - 1)]; fup->fu_next = *fhp; *fhp = fup; fup->fu_id = id; if (name) bcopy(name, fup->fu_name, len + 1); else { (void)sprintf(fup->fu_name, "%lu", id); if (vflag) { if (aflag && fsname != NULL) (void)printf("%s: ", fsname); printf("unknown %cid: %lu\n", type == USRQUOTA ? 'u' : 'g', id); } } return (fup); } /* * Special purpose version of ginode used to optimize pass * over all the inodes in numerical order. */ static ino_t nextino, lastinum, lastvalidinum; static long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize; static caddr_t inodebuf; #define INOBUFSIZE 56*1024 /* size of buffer to read inodes */ union dinode * getnextinode(ino_t inumber) { long size; ufs2_daddr_t dblk; union dinode *dp; static caddr_t nextinop; if (inumber != nextino++ || inumber > lastvalidinum) errx(1, "bad inode number %ju to nextinode", (uintmax_t)inumber); if (inumber >= lastinum) { readcnt++; dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum)); if (readcnt % readpercg == 0) { size = partialsize; lastinum += partialcnt; } else { size = inobufsize; lastinum += fullcnt; } /* * If blkread returns an error, it will already have zeroed * out the buffer, so we do not need to do so here. */ blkread(dblk, inodebuf, size); nextinop = inodebuf; } dp = (union dinode *)nextinop; if (sblock.fs_magic == FS_UFS1_MAGIC) nextinop += sizeof(struct ufs1_dinode); else nextinop += sizeof(struct ufs2_dinode); return (dp); } /* * Prepare to scan a set of inodes. */ void setinodebuf(ino_t inum) { if (inum % sblock.fs_ipg != 0) errx(1, "bad inode number %ju to setinodebuf", (uintmax_t)inum); lastvalidinum = inum + sblock.fs_ipg - 1; nextino = inum; lastinum = inum; readcnt = 0; if (inodebuf != NULL) return; inobufsize = blkroundup(&sblock, INOBUFSIZE); fullcnt = inobufsize / ((sblock.fs_magic == FS_UFS1_MAGIC) ? sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode)); readpercg = sblock.fs_ipg / fullcnt; partialcnt = sblock.fs_ipg % fullcnt; partialsize = partialcnt * ((sblock.fs_magic == FS_UFS1_MAGIC) ? sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode)); if (partialcnt != 0) { readpercg++; } else { partialcnt = fullcnt; partialsize = inobufsize; } if ((inodebuf = malloc((unsigned)inobufsize)) == NULL) errx(1, "cannot allocate space for inode buffer"); } /* * Free up data structures used to scan inodes. */ void freeinodebuf(void) { if (inodebuf != NULL) free(inodebuf); inodebuf = NULL; } /* * Read specified disk blocks. */ void blkread(ufs2_daddr_t bno, char *buf, long cnt) { if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0 || read(fi, buf, cnt) != cnt) errx(1, "blkread failed on block %ld", (long)bno); } /* * Display updated block and i-node counts. */ void printchanges(const char *fsname, int type, struct dqblk *dp, struct fileusage *fup, u_long id) { if (!vflag) return; if (aflag) (void)printf("%s: ", fsname); if (fup->fu_name[0] == '\0') (void)printf("%-8lu fixed ", id); else (void)printf("%-8s fixed ", fup->fu_name); switch (type) { case GRPQUOTA: (void)printf("(group):"); break; case USRQUOTA: (void)printf("(user): "); break; default: (void)printf("(unknown quota type %d)", type); break; } if (dp->dqb_curinodes != fup->fu_curinodes) (void)printf("\tinodes %lu -> %lu", (u_long)dp->dqb_curinodes, (u_long)fup->fu_curinodes); if (dp->dqb_curblocks != fup->fu_curblocks) (void)printf("\tblocks %lu -> %lu", (u_long)dp->dqb_curblocks, (u_long)fup->fu_curblocks); (void)printf("\n"); } diff --git a/stand/libsa/ufs.c b/stand/libsa/ufs.c index 12703a3e18aa..de3dbe58789b 100644 --- a/stand/libsa/ufs.c +++ b/stand/libsa/ufs.c @@ -1,958 +1,958 @@ /* $NetBSD: ufs.c,v 1.20 1998/03/01 07:15:39 ross 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. * * This code is derived from software contributed to Berkeley by * The Mach Operating System project at Carnegie-Mellon University. * * 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) 1990, 1991 Carnegie Mellon University * All Rights Reserved. * * Author: David Golub * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ #include __FBSDID("$FreeBSD$"); /* * Stand-alone file reading package. */ #include #include #include #include #include #include #include "stand.h" #include "disk.h" #include "string.h" static int ufs_open(const char *path, struct open_file *f); static int ufs_write(struct open_file *f, const void *buf, size_t size, size_t *resid); static int ufs_close(struct open_file *f); static int ufs_read(struct open_file *f, void *buf, size_t size, size_t *resid); static off_t ufs_seek(struct open_file *f, off_t offset, int where); static int ufs_stat(struct open_file *f, struct stat *sb); static int ufs_readdir(struct open_file *f, struct dirent *d); static int ufs_mount(const char *dev, const char *path, void **data); static int ufs_unmount(const char *dev, void *data); struct fs_ops ufs_fsops = { .fs_name = "ufs", .fo_open = ufs_open, .fo_close = ufs_close, .fo_read = ufs_read, .fo_write = ufs_write, .fo_seek = ufs_seek, .fo_stat = ufs_stat, .fo_readdir = ufs_readdir, .fo_mount = ufs_mount, .fo_unmount = ufs_unmount }; /* * In-core open file. */ struct file { off_t f_seekp; /* seek pointer */ struct fs *f_fs; /* pointer to super-block */ union dinode { struct ufs1_dinode di1; struct ufs2_dinode di2; } f_di; /* copy of on-disk inode */ int f_nindir[UFS_NIADDR]; /* number of blocks mapped by indirect block at level i */ char *f_blk[UFS_NIADDR]; /* buffer for indirect block at level i */ size_t f_blksize[UFS_NIADDR]; /* size of buffer */ ufs2_daddr_t f_blkno[UFS_NIADDR];/* disk address of block in buffer */ ufs2_daddr_t f_buf_blkno; /* block number of data block */ char *f_buf; /* buffer for data block */ size_t f_buf_size; /* size of data block */ int f_inumber; /* inumber */ }; #define DIP(fp, field) \ ((fp)->f_fs->fs_magic == FS_UFS1_MAGIC ? \ (fp)->f_di.di1.field : (fp)->f_di.di2.field) typedef struct ufs_mnt { char *um_dev; int um_fd; STAILQ_ENTRY(ufs_mnt) um_link; } ufs_mnt_t; typedef STAILQ_HEAD(ufs_mnt_list, ufs_mnt) ufs_mnt_list_t; static ufs_mnt_list_t mnt_list = STAILQ_HEAD_INITIALIZER(mnt_list); static int read_inode(ino_t, struct open_file *); static int block_map(struct open_file *, ufs2_daddr_t, ufs2_daddr_t *); static int buf_read_file(struct open_file *, char **, size_t *); static int buf_write_file(struct open_file *, const char *, size_t *); static int search_directory(char *, struct open_file *, ino_t *); static int ufs_use_sa_read(void *, off_t, void **, int); /* from ffs_subr.c */ -int ffs_sbget(void *, struct fs **, off_t, char *, +int ffs_sbget(void *, struct fs **, off_t, int, char *, int (*)(void *, off_t, void **, int)); /* * Read a new inode into a file structure. */ static int read_inode(ino_t inumber, struct open_file *f) { struct file *fp = (struct file *)f->f_fsdata; struct fs *fs = fp->f_fs; char *buf; size_t rsize; int rc; if (fs == NULL) panic("fs == NULL"); /* * Read inode and save it. */ buf = malloc(fs->fs_bsize); twiddle(1); rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, fsbtodb(fs, ino_to_fsba(fs, inumber)), fs->fs_bsize, buf, &rsize); if (rc) goto out; if (rsize != fs->fs_bsize) { rc = EIO; goto out; } if (fp->f_fs->fs_magic == FS_UFS1_MAGIC) fp->f_di.di1 = ((struct ufs1_dinode *)buf) [ino_to_fsbo(fs, inumber)]; else fp->f_di.di2 = ((struct ufs2_dinode *)buf) [ino_to_fsbo(fs, inumber)]; /* * Clear out the old buffers */ { int level; for (level = 0; level < UFS_NIADDR; level++) fp->f_blkno[level] = -1; fp->f_buf_blkno = -1; } fp->f_seekp = 0; fp->f_inumber = inumber; out: free(buf); return (rc); } /* * Given an offset in a file, find the disk block number that * contains that block. */ static int block_map(struct open_file *f, ufs2_daddr_t file_block, ufs2_daddr_t *disk_block_p) { struct file *fp = (struct file *)f->f_fsdata; struct fs *fs = fp->f_fs; int level; int idx; ufs2_daddr_t ind_block_num; int rc; /* * Index structure of an inode: * * di_db[0..UFS_NDADDR-1] hold block numbers for blocks * 0..UFS_NDADDR-1 * * di_ib[0] index block 0 is the single indirect block * holds block numbers for blocks * UFS_NDADDR .. UFS_NDADDR + NINDIR(fs)-1 * * di_ib[1] index block 1 is the double indirect block * holds block numbers for INDEX blocks for blocks * UFS_NDADDR + NINDIR(fs) .. * UFS_NDADDR + NINDIR(fs) + NINDIR(fs)**2 - 1 * * di_ib[2] index block 2 is the triple indirect block * holds block numbers for double-indirect * blocks for blocks * UFS_NDADDR + NINDIR(fs) + NINDIR(fs)**2 .. * UFS_NDADDR + NINDIR(fs) + NINDIR(fs)**2 * + NINDIR(fs)**3 - 1 */ if (file_block < UFS_NDADDR) { /* Direct block. */ *disk_block_p = DIP(fp, di_db[file_block]); return (0); } file_block -= UFS_NDADDR; /* * nindir[0] = NINDIR * nindir[1] = NINDIR**2 * nindir[2] = NINDIR**3 * etc */ for (level = 0; level < UFS_NIADDR; level++) { if (file_block < fp->f_nindir[level]) break; file_block -= fp->f_nindir[level]; } if (level == UFS_NIADDR) { /* Block number too high */ return (EFBIG); } ind_block_num = DIP(fp, di_ib[level]); for (; level >= 0; level--) { if (ind_block_num == 0) { *disk_block_p = 0; /* missing */ return (0); } if (fp->f_blkno[level] != ind_block_num) { if (fp->f_blk[level] == (char *)0) fp->f_blk[level] = malloc(fs->fs_bsize); twiddle(1); rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, fsbtodb(fp->f_fs, ind_block_num), fs->fs_bsize, fp->f_blk[level], &fp->f_blksize[level]); if (rc) return (rc); if (fp->f_blksize[level] != fs->fs_bsize) return (EIO); fp->f_blkno[level] = ind_block_num; } if (level > 0) { idx = file_block / fp->f_nindir[level - 1]; file_block %= fp->f_nindir[level - 1]; } else idx = file_block; if (fp->f_fs->fs_magic == FS_UFS1_MAGIC) ind_block_num = ((ufs1_daddr_t *)fp->f_blk[level])[idx]; else ind_block_num = ((ufs2_daddr_t *)fp->f_blk[level])[idx]; } *disk_block_p = ind_block_num; return (0); } /* * Write a portion of a file from an internal buffer. */ static int buf_write_file(struct open_file *f, const char *buf_p, size_t *size_p) { struct file *fp = (struct file *)f->f_fsdata; struct fs *fs = fp->f_fs; long off; ufs_lbn_t file_block; ufs2_daddr_t disk_block; size_t block_size; int rc; /* * Calculate the starting block address and offset. */ off = blkoff(fs, fp->f_seekp); file_block = lblkno(fs, fp->f_seekp); block_size = sblksize(fs, DIP(fp, di_size), file_block); rc = block_map(f, file_block, &disk_block); if (rc) return (rc); if (disk_block == 0) /* Because we can't allocate space on the drive */ return (EFBIG); /* * Truncate buffer at end of file, and at the end of * this block. */ if (*size_p > DIP(fp, di_size) - fp->f_seekp) *size_p = DIP(fp, di_size) - fp->f_seekp; if (*size_p > block_size - off) *size_p = block_size - off; /* * If we don't entirely occlude the block and it's not * in memory already, read it in first. */ if (((off > 0) || (*size_p + off < block_size)) && (file_block != fp->f_buf_blkno)) { if (fp->f_buf == (char *)0) fp->f_buf = malloc(fs->fs_bsize); twiddle(4); rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, fsbtodb(fs, disk_block), block_size, fp->f_buf, &fp->f_buf_size); if (rc) return (rc); fp->f_buf_blkno = file_block; } /* * Copy the user data into the cached block. */ bcopy(buf_p, fp->f_buf + off, *size_p); /* * Write the block out to storage. */ twiddle(4); rc = (f->f_dev->dv_strategy)(f->f_devdata, F_WRITE, fsbtodb(fs, disk_block), block_size, fp->f_buf, &fp->f_buf_size); return (rc); } /* * Read a portion of a file into an internal buffer. Return * the location in the buffer and the amount in the buffer. */ static int buf_read_file(struct open_file *f, char **buf_p, size_t *size_p) { struct file *fp = (struct file *)f->f_fsdata; struct fs *fs = fp->f_fs; long off; ufs_lbn_t file_block; ufs2_daddr_t disk_block; size_t block_size; int rc; off = blkoff(fs, fp->f_seekp); file_block = lblkno(fs, fp->f_seekp); block_size = sblksize(fs, DIP(fp, di_size), file_block); if (file_block != fp->f_buf_blkno) { if (fp->f_buf == (char *)0) fp->f_buf = malloc(fs->fs_bsize); rc = block_map(f, file_block, &disk_block); if (rc) return (rc); if (disk_block == 0) { bzero(fp->f_buf, block_size); fp->f_buf_size = block_size; } else { twiddle(4); rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, fsbtodb(fs, disk_block), block_size, fp->f_buf, &fp->f_buf_size); if (rc) return (rc); } fp->f_buf_blkno = file_block; } /* * Return address of byte in buffer corresponding to * offset, and size of remainder of buffer after that * byte. */ *buf_p = fp->f_buf + off; *size_p = block_size - off; /* * But truncate buffer at end of file. */ if (*size_p > DIP(fp, di_size) - fp->f_seekp) *size_p = DIP(fp, di_size) - fp->f_seekp; return (0); } /* * Search a directory for a name and return its * i_number. */ static int search_directory(char *name, struct open_file *f, ino_t *inumber_p) { struct file *fp = (struct file *)f->f_fsdata; struct direct *dp; struct direct *edp; char *buf; size_t buf_size; int namlen, length; int rc; length = strlen(name); fp->f_seekp = 0; while (fp->f_seekp < DIP(fp, di_size)) { rc = buf_read_file(f, &buf, &buf_size); if (rc) return (rc); dp = (struct direct *)buf; edp = (struct direct *)(buf + buf_size); while (dp < edp) { if (dp->d_ino == (ino_t)0) goto next; #if BYTE_ORDER == LITTLE_ENDIAN if (fp->f_fs->fs_maxsymlinklen <= 0) namlen = dp->d_type; else #endif namlen = dp->d_namlen; if (namlen == length && !strcmp(name, dp->d_name)) { /* found entry */ *inumber_p = dp->d_ino; return (0); } next: dp = (struct direct *)((char *)dp + dp->d_reclen); } fp->f_seekp += buf_size; } return (ENOENT); } /* * Open a file. */ static int ufs_open(const char *upath, struct open_file *f) { char *cp, *ncp; int c; ino_t inumber, parent_inumber; struct file *fp; struct fs *fs; int rc; int nlinks = 0; char namebuf[MAXPATHLEN+1]; char *buf = NULL; char *path = NULL; const char *dev; ufs_mnt_t *mnt; /* allocate file system specific data structure */ errno = 0; fp = calloc(1, sizeof(struct file)); if (fp == NULL) return (errno); f->f_fsdata = (void *)fp; dev = disk_fmtdev(f->f_devdata); /* Is this device mounted? */ STAILQ_FOREACH(mnt, &mnt_list, um_link) { if (strcmp(dev, mnt->um_dev) == 0) break; } if (mnt == NULL) { /* read super block */ twiddle(1); - if ((rc = ffs_sbget(f, &fs, STDSB_NOHASHFAIL, "stand", + if ((rc = ffs_sbget(f, &fs, UFS_STDSB, UFS_NOHASHFAIL, "stand", ufs_use_sa_read)) != 0) { goto out; } } else { struct open_file *sbf; struct file *sfp; /* get superblock from mounted file system */ sbf = fd2open_file(mnt->um_fd); sfp = sbf->f_fsdata; fs = sfp->f_fs; } fp->f_fs = fs; /* * Calculate indirect block levels. */ { ufs2_daddr_t mult; int level; mult = 1; for (level = 0; level < UFS_NIADDR; level++) { mult *= NINDIR(fs); fp->f_nindir[level] = mult; } } inumber = UFS_ROOTINO; if ((rc = read_inode(inumber, f)) != 0) goto out; cp = path = strdup(upath); if (path == NULL) { rc = ENOMEM; goto out; } while (*cp) { /* * Remove extra separators */ while (*cp == '/') cp++; if (*cp == '\0') break; /* * Check that current node is a directory. */ if ((DIP(fp, di_mode) & IFMT) != IFDIR) { rc = ENOTDIR; goto out; } /* * Get next component of path name. */ { int len = 0; ncp = cp; while ((c = *cp) != '\0' && c != '/') { if (++len > UFS_MAXNAMLEN) { rc = ENOENT; goto out; } cp++; } *cp = '\0'; } /* * Look up component in current directory. * Save directory inumber in case we find a * symbolic link. */ parent_inumber = inumber; rc = search_directory(ncp, f, &inumber); *cp = c; if (rc) goto out; /* * Open next component. */ if ((rc = read_inode(inumber, f)) != 0) goto out; /* * Check for symbolic link. */ if ((DIP(fp, di_mode) & IFMT) == IFLNK) { int link_len = DIP(fp, di_size); int len; len = strlen(cp); if (link_len + len > MAXPATHLEN || ++nlinks > MAXSYMLINKS) { rc = ENOENT; goto out; } bcopy(cp, &namebuf[link_len], len + 1); if (link_len < fs->fs_maxsymlinklen) { bcopy(DIP(fp, di_shortlink), namebuf, (unsigned) link_len); } else { /* * Read file for symbolic link */ size_t buf_size; ufs2_daddr_t disk_block; struct fs *fs = fp->f_fs; if (!buf) buf = malloc(fs->fs_bsize); rc = block_map(f, (ufs2_daddr_t)0, &disk_block); if (rc) goto out; twiddle(1); rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, fsbtodb(fs, disk_block), fs->fs_bsize, buf, &buf_size); if (rc) goto out; bcopy((char *)buf, namebuf, (unsigned)link_len); } /* * If relative pathname, restart at parent directory. * If absolute pathname, restart at root. */ cp = namebuf; if (*cp != '/') inumber = parent_inumber; else inumber = (ino_t)UFS_ROOTINO; if ((rc = read_inode(inumber, f)) != 0) goto out; } } /* * Found terminal component. */ rc = 0; fp->f_seekp = 0; out: free(buf); free(path); if (rc) { free(fp->f_buf); if (mnt == NULL && fp->f_fs != NULL) { free(fp->f_fs->fs_csp); free(fp->f_fs->fs_si); free(fp->f_fs); } free(fp); } return (rc); } /* * A read function for use by standalone-layer routines. */ static int ufs_use_sa_read(void *devfd, off_t loc, void **bufp, int size) { struct open_file *f; size_t buf_size; int error; f = (struct open_file *)devfd; if ((*bufp = malloc(size)) == NULL) return (ENOSPC); error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, loc / DEV_BSIZE, size, *bufp, &buf_size); if (error != 0) return (error); if (buf_size != size) return (EIO); return (0); } static int ufs_close(struct open_file *f) { ufs_mnt_t *mnt; struct file *fp = (struct file *)f->f_fsdata; int level; char *dev; f->f_fsdata = NULL; if (fp == NULL) return (0); for (level = 0; level < UFS_NIADDR; level++) { free(fp->f_blk[level]); } free(fp->f_buf); dev = disk_fmtdev(f->f_devdata); STAILQ_FOREACH(mnt, &mnt_list, um_link) { if (strcmp(dev, mnt->um_dev) == 0) break; } if (mnt == NULL && fp->f_fs != NULL) { free(fp->f_fs->fs_csp); free(fp->f_fs->fs_si); free(fp->f_fs); } free(fp); return (0); } /* * Copy a portion of a file into kernel memory. * Cross block boundaries when necessary. */ static int ufs_read(struct open_file *f, void *start, size_t size, size_t *resid) { struct file *fp = (struct file *)f->f_fsdata; size_t csize; char *buf; size_t buf_size; int rc = 0; char *addr = start; while (size != 0) { if (fp->f_seekp >= DIP(fp, di_size)) break; rc = buf_read_file(f, &buf, &buf_size); if (rc) break; csize = size; if (csize > buf_size) csize = buf_size; bcopy(buf, addr, csize); fp->f_seekp += csize; addr += csize; size -= csize; } if (resid) *resid = size; return (rc); } /* * Write to a portion of an already allocated file. * Cross block boundaries when necessary. Can not * extend the file. */ static int ufs_write(struct open_file *f, const void *start, size_t size, size_t *resid) { struct file *fp = (struct file *)f->f_fsdata; size_t csize; int rc = 0; const char *addr = start; csize = size; while ((size != 0) && (csize != 0)) { if (fp->f_seekp >= DIP(fp, di_size)) break; if (csize >= 512) csize = 512; /* XXX */ rc = buf_write_file(f, addr, &csize); if (rc) break; fp->f_seekp += csize; addr += csize; size -= csize; } if (resid) *resid = size; return (rc); } static off_t ufs_seek(struct open_file *f, off_t offset, int where) { struct file *fp = (struct file *)f->f_fsdata; switch (where) { case SEEK_SET: fp->f_seekp = offset; break; case SEEK_CUR: fp->f_seekp += offset; break; case SEEK_END: fp->f_seekp = DIP(fp, di_size) - offset; break; default: errno = EINVAL; return (-1); } return (fp->f_seekp); } static int ufs_stat(struct open_file *f, struct stat *sb) { struct file *fp = (struct file *)f->f_fsdata; /* only important stuff */ sb->st_mode = DIP(fp, di_mode); sb->st_uid = DIP(fp, di_uid); sb->st_gid = DIP(fp, di_gid); sb->st_size = DIP(fp, di_size); sb->st_mtime = DIP(fp, di_mtime); /* * The items below are ufs specific! * Other fs types will need their own solution * if these fields are needed. */ sb->st_ino = fp->f_inumber; /* * We need something to differentiate devs. * fs_id is unique but 64bit, we xor the two * halves to squeeze it into 32bits. */ sb->st_dev = (dev_t)(fp->f_fs->fs_id[0] ^ fp->f_fs->fs_id[1]); return (0); } static int ufs_readdir(struct open_file *f, struct dirent *d) { struct file *fp = (struct file *)f->f_fsdata; struct direct *dp; char *buf; size_t buf_size; int error; /* * assume that a directory entry will not be split across blocks */ again: if (fp->f_seekp >= DIP(fp, di_size)) return (ENOENT); error = buf_read_file(f, &buf, &buf_size); if (error) return (error); dp = (struct direct *)buf; fp->f_seekp += dp->d_reclen; if (dp->d_ino == (ino_t)0) goto again; d->d_type = dp->d_type; strcpy(d->d_name, dp->d_name); return (0); } static int ufs_mount(const char *dev, const char *path, void **data) { char *fs; ufs_mnt_t *mnt; struct open_file *f; errno = 0; mnt = calloc(1, sizeof(*mnt)); if (mnt == NULL) return (errno); mnt->um_fd = -1; mnt->um_dev = strdup(dev); if (mnt->um_dev == NULL) goto done; if (asprintf(&fs, "%s%s", dev, path) < 0) goto done; mnt->um_fd = open(fs, O_RDONLY); free(fs); if (mnt->um_fd == -1) goto done; /* Is it ufs file system? */ f = fd2open_file(mnt->um_fd); if (strcmp(f->f_ops->fs_name, "ufs") == 0) STAILQ_INSERT_TAIL(&mnt_list, mnt, um_link); else errno = ENXIO; done: if (errno != 0) { free(mnt->um_dev); if (mnt->um_fd >= 0) close(mnt->um_fd); free(mnt); } else { *data = mnt; } return (errno); } static int ufs_unmount(const char *dev __unused, void *data) { ufs_mnt_t *mnt = data; STAILQ_REMOVE(&mnt_list, mnt, ufs_mnt, um_link); free(mnt->um_dev); close(mnt->um_fd); free(mnt); return (0); } diff --git a/sys/geom/journal/g_journal_ufs.c b/sys/geom/journal/g_journal_ufs.c index dec28e547f82..dc9604abae22 100644 --- a/sys/geom/journal/g_journal_ufs.c +++ b/sys/geom/journal/g_journal_ufs.c @@ -1,112 +1,113 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2005-2006 Pawel Jakub Dawidek * 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 AUTHORS 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 AUTHORS 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int g_journal_ufs_clean(struct mount *mp) { struct ufsmount *ump; struct fs *fs; int flags; ump = VFSTOUFS(mp); fs = ump->um_fs; flags = fs->fs_flags; fs->fs_flags &= ~(FS_UNCLEAN | FS_NEEDSFSCK); ffs_sbupdate(ump, MNT_WAIT, 1); fs->fs_flags = flags; return (0); } static void g_journal_ufs_dirty(struct g_consumer *cp) { struct fs *fs; int error; fs = NULL; if (SBLOCKSIZE % cp->provider->sectorsize != 0 || - ffs_sbget(cp, &fs, STDSB, M_GEOM, g_use_g_read_data) != 0) { + ffs_sbget(cp, &fs, UFS_STDSB, UFS_NOCSUM, M_GEOM, g_use_g_read_data) + != 0) { GJ_DEBUG(0, "Cannot find superblock to mark file system %s " "as dirty.", cp->provider->name); KASSERT(fs == NULL, ("g_journal_ufs_dirty: non-NULL fs %p\n", fs)); return; } /* * Do not use or change summary information, so free it now * to avoid unnecessarily writing it back out in ffs_sbput(). */ g_free(fs->fs_csp); g_free(fs->fs_si); fs->fs_si = NULL; GJ_DEBUG(0, "clean=%d flags=0x%x", fs->fs_clean, fs->fs_flags); fs->fs_clean = 0; fs->fs_flags |= FS_NEEDSFSCK | FS_UNCLEAN; error = ffs_sbput(cp, fs, fs->fs_sblockloc, g_use_g_write_data); g_free(fs); if (error != 0) { GJ_DEBUG(0, "Cannot mark file system %s as dirty " "(error=%d).", cp->provider->name, error); } else { GJ_DEBUG(0, "File system %s marked as dirty.", cp->provider->name); } } const struct g_journal_desc g_journal_ufs = { .jd_fstype = "ufs", .jd_clean = g_journal_ufs_clean, .jd_dirty = g_journal_ufs_dirty }; MODULE_DEPEND(g_journal, ufs, 1, 1, 1); MODULE_VERSION(geom_journal, 0); diff --git a/sys/geom/label/g_label_ufs.c b/sys/geom/label/g_label_ufs.c index 0de09ec93896..691f875cf6cc 100644 --- a/sys/geom/label/g_label_ufs.c +++ b/sys/geom/label/g_label_ufs.c @@ -1,209 +1,208 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2002, 2003 Gordon Tetlow * Copyright (c) 2006 Pawel Jakub Dawidek * 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 AUTHORS 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 AUTHORS 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #define G_LABEL_UFS_VOLUME 0 #define G_LABEL_UFS_ID 1 /* * G_LABEL_UFS_CMP returns true if difference between provider mediasize * and filesystem size is less than G_LABEL_UFS_MAXDIFF sectors */ #define G_LABEL_UFS_CMP(prov, fsys, size) \ ( abs( ((fsys)->size) - ( (prov)->mediasize / (fsys)->fs_fsize )) \ < G_LABEL_UFS_MAXDIFF ) #define G_LABEL_UFS_MAXDIFF 0x100 /* * For providers that look like disklabels we need to check if the file system * size is almost equal to the provider's size, because sysinstall(8) used to * bogusly put the first partition at offset 0 instead of 16, and glabel/ufs * would find a file system on the slice instead of the partition. * * In addition, media size can be a bit bigger than file system size. For * instance, mkuzip can append bytes to align data to large sector size (it * improves compression rates). */ static bool g_label_ufs_ignore_bsdlabel_slice(struct g_consumer *cp, struct fs *fs) { struct g_provider *pp; u_char *buf; uint32_t magic1, magic2; int error; pp = cp->provider; /* * If the expected provider size for the filesystem matches the * real provider size then don't ignore this filesystem. */ if (G_LABEL_UFS_CMP(pp, fs, fs_providersize)) return (false); /* * If the filesystem size matches the real provider size then * don't ignore this filesystem. */ if (fs->fs_magic == FS_UFS1_MAGIC ? G_LABEL_UFS_CMP(pp, fs, fs_old_size) : G_LABEL_UFS_CMP(pp, fs, fs_size)) return (false); /* * Provider is bigger than expected; probe to see if there's a * disklabel. Adapted from g_part_bsd_probe. */ /* Check if the superblock overlaps where the disklabel lives. */ if (fs->fs_sblockloc < pp->sectorsize * 2) return (false); /* Sanity-check the provider. */ if (pp->sectorsize < sizeof(struct disklabel) || pp->mediasize < BBSIZE) return (false); if (BBSIZE % pp->sectorsize) return (false); /* Check that there's a disklabel. */ buf = g_read_data(cp, pp->sectorsize, pp->sectorsize, &error); if (buf == NULL) return (false); magic1 = le32dec(buf + 0); magic2 = le32dec(buf + 132); g_free(buf); if (magic1 == DISKMAGIC && magic2 == DISKMAGIC) return (true); return (false); } /* * Try to find a superblock on the provider. If successful, look for a volume * label and create an appropriate provider based on that. */ static void g_label_ufs_taste_common(struct g_consumer *cp, char *label, size_t size, int what) { struct g_provider *pp; struct fs *fs; g_topology_assert_not(); pp = cp->provider; label[0] = '\0'; fs = NULL; - if (SBLOCKSIZE % pp->sectorsize != 0 || ffs_sbget(cp, &fs, - STDSB_NOHASHFAIL_NOMSG, M_GEOM, g_use_g_read_data) != 0) { + if (SBLOCKSIZE % pp->sectorsize != 0 || ffs_sbget(cp, &fs, UFS_STDSB, + UFS_NOHASHFAIL | UFS_NOCSUM | UFS_NOMSG, M_GEOM, g_use_g_read_data) + != 0) { KASSERT(fs == NULL, ("g_label_ufs_taste_common: non-NULL fs %p\n", fs)); return; } /* Check for magic. */ if (fs->fs_magic == FS_UFS1_MAGIC && fs->fs_fsize > 0) { /* Valid UFS1. */ } else if (fs->fs_magic == FS_UFS2_MAGIC && fs->fs_fsize > 0) { /* Valid UFS2. */ } else { goto out; } /* Check if this should be ignored for compatibility. */ if (g_label_ufs_ignore_bsdlabel_slice(cp, fs)) goto out; G_LABEL_DEBUG(1, "%s file system detected on %s.", fs->fs_magic == FS_UFS1_MAGIC ? "UFS1" : "UFS2", pp->name); switch (what) { case G_LABEL_UFS_VOLUME: /* Check for volume label */ if (fs->fs_volname[0] != '\0') strlcpy(label, fs->fs_volname, size); break; case G_LABEL_UFS_ID: if (fs->fs_id[0] != 0 || fs->fs_id[1] != 0) snprintf(label, size, "%08x%08x", fs->fs_id[0], fs->fs_id[1]); break; } out: - g_free(fs->fs_csp); - g_free(fs->fs_si); g_free(fs); } static void g_label_ufs_volume_taste(struct g_consumer *cp, char *label, size_t size) { g_label_ufs_taste_common(cp, label, size, G_LABEL_UFS_VOLUME); } static void g_label_ufs_id_taste(struct g_consumer *cp, char *label, size_t size) { g_label_ufs_taste_common(cp, label, size, G_LABEL_UFS_ID); } struct g_label_desc g_label_ufs_volume = { .ld_taste = g_label_ufs_volume_taste, .ld_dirprefix = "ufs/", .ld_enabled = 1 }; struct g_label_desc g_label_ufs_id = { .ld_taste = g_label_ufs_id_taste, .ld_dirprefix = "ufsid/", .ld_enabled = 1 }; G_LABEL_INIT(ufsid, g_label_ufs_id, "Create device nodes for UFS file system IDs"); G_LABEL_INIT(ufs, g_label_ufs_volume, "Create device nodes for UFS volume names"); MODULE_DEPEND(g_label, ufs, 1, 1, 1); diff --git a/sys/ufs/ffs/ffs_extern.h b/sys/ufs/ffs/ffs_extern.h index fdc08dc6aafb..54820fc5b3ce 100644 --- a/sys/ufs/ffs/ffs_extern.h +++ b/sys/ufs/ffs/ffs_extern.h @@ -1,242 +1,242 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * 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 * $FreeBSD$ */ #ifndef _UFS_FFS_EXTERN_H #define _UFS_FFS_EXTERN_H #ifndef _KERNEL #error "No user-serving parts inside" #else struct buf; struct cg; struct fid; struct fs; struct inode; struct malloc_type; struct mount; struct thread; struct sockaddr; struct statfs; struct ucred; struct vnode; struct vop_fsync_args; struct vop_reallocblks_args; struct workhead; int ffs_alloc(struct inode *, ufs2_daddr_t, ufs2_daddr_t, int, int, struct ucred *, ufs2_daddr_t *); int ffs_balloc_ufs1(struct vnode *a_vp, off_t a_startoffset, int a_size, struct ucred *a_cred, int a_flags, struct buf **a_bpp); int ffs_balloc_ufs2(struct vnode *a_vp, off_t a_startoffset, int a_size, struct ucred *a_cred, int a_flags, struct buf **a_bpp); void ffs_blkfree(struct ufsmount *, struct fs *, struct vnode *, ufs2_daddr_t, long, ino_t, enum vtype, struct workhead *, u_long); ufs2_daddr_t ffs_blkpref_ufs1(struct inode *, ufs_lbn_t, int, ufs1_daddr_t *); ufs2_daddr_t ffs_blkpref_ufs2(struct inode *, ufs_lbn_t, int, ufs2_daddr_t *); void ffs_blkrelease_finish(struct ufsmount *, u_long); u_long ffs_blkrelease_start(struct ufsmount *, struct vnode *, ino_t); uint32_t ffs_calc_sbhash(struct fs *); int ffs_checkfreefile(struct fs *, struct vnode *, ino_t); void ffs_clrblock(struct fs *, u_char *, ufs1_daddr_t); void ffs_clusteracct(struct fs *, struct cg *, ufs1_daddr_t, int); void ffs_bdflush(struct bufobj *, struct buf *); int ffs_copyonwrite(struct vnode *, struct buf *); int ffs_flushfiles(struct mount *, int, struct thread *); void ffs_fragacct(struct fs *, int, int32_t [], int); int ffs_freefile(struct ufsmount *, struct fs *, struct vnode *, ino_t, int, struct workhead *); void ffs_fserr(struct fs *, ino_t, char *); int ffs_getcg(struct fs *, struct vnode *, u_int, int, struct buf **, struct cg **); int ffs_inotovp(struct mount *, ino_t, u_int64_t, int, struct vnode **, int); int ffs_isblock(struct fs *, u_char *, ufs1_daddr_t); int ffs_isfreeblock(struct fs *, u_char *, ufs1_daddr_t); void ffs_oldfscompat_write(struct fs *, struct ufsmount *); int ffs_own_mount(const struct mount *mp); int ffs_reallocblks(struct vop_reallocblks_args *); int ffs_realloccg(struct inode *, ufs2_daddr_t, ufs2_daddr_t, ufs2_daddr_t, int, int, int, struct ucred *, struct buf **); int ffs_reload(struct mount *, int); -int ffs_sbget(void *, struct fs **, off_t, struct malloc_type *, +int ffs_sbget(void *, struct fs **, off_t, int, struct malloc_type *, int (*)(void *, off_t, void **, int)); int ffs_sbput(void *, struct fs *, off_t, int (*)(void *, off_t, void *, int)); int ffs_sbupdate(struct ufsmount *, int, int); void ffs_setblock(struct fs *, u_char *, ufs1_daddr_t); int ffs_snapblkfree(struct fs *, struct vnode *, ufs2_daddr_t, long, ino_t, enum vtype, struct workhead *); void ffs_snapremove(struct vnode *vp); int ffs_snapshot(struct mount *mp, char *snapfile); void ffs_snapshot_mount(struct mount *mp); void ffs_snapshot_unmount(struct mount *mp); void ffs_susp_initialize(void); void ffs_susp_uninitialize(void); void ffs_sync_snap(struct mount *, int); int ffs_syncvnode(struct vnode *vp, int waitfor, int flags); int ffs_truncate(struct vnode *, off_t, int, struct ucred *); int ffs_update(struct vnode *, int); void ffs_update_dinode_ckhash(struct fs *, struct ufs2_dinode *); int ffs_verify_dinode_ckhash(struct fs *, struct ufs2_dinode *); int ffs_valloc(struct vnode *, int, struct ucred *, struct vnode **); int ffs_vfree(struct vnode *, ino_t, int); vfs_vget_t ffs_vget; int ffs_vgetf(struct mount *, ino_t, int, struct vnode **, int); void process_deferred_inactive(struct mount *mp); int ffs_fsfail_cleanup(struct ufsmount *, int); int ffs_fsfail_cleanup_locked(struct ufsmount *, int); int ffs_breadz(struct ufsmount *, struct vnode *, daddr_t, daddr_t, int, daddr_t *, int *, int, struct ucred *, int, void (*)(struct buf *), struct buf **); /* * Flags to ffs_vgetf */ #define FFSV_FORCEINSMQ 0x0001 /* Force insertion into mount list */ #define FFSV_REPLACE 0x0002 /* Replace existing vnode */ #define FFSV_REPLACE_DOOMED 0x0004 /* Replace existing vnode if it is doomed */ #define FFSV_FORCEINODEDEP 0x0008 /* Force allocation of inodedep, ignore MNT_SOFTDEP */ /* * Flags to ffs_reload */ #define FFSR_FORCE 0x0001 #define FFSR_UNSUSPEND 0x0002 /* * Definitions for TRIM interface * * Special keys and recommended hash table size */ #define NOTRIM_KEY 1 /* never written, so don't call trim for it */ #define SINGLETON_KEY 2 /* only block being freed, so trim it now */ #define FIRST_VALID_KEY 3 /* first valid key describing a block range */ #define MAXTRIMIO 1024 /* maximum expected outstanding trim requests */ extern struct vop_vector ffs_vnodeops1; extern struct vop_vector ffs_fifoops1; extern struct vop_vector ffs_vnodeops2; extern struct vop_vector ffs_fifoops2; /* * Soft update function prototypes. */ int softdep_check_suspend(struct mount *, struct vnode *, int, int, int, int); void softdep_get_depcounts(struct mount *, int *, int *); void softdep_initialize(void); void softdep_uninitialize(void); int softdep_mount(struct vnode *, struct mount *, struct fs *, struct ucred *); void softdep_unmount(struct mount *); void softdep_handle_error(struct buf *); int softdep_move_dependencies(struct buf *, struct buf *); int softdep_flushworklist(struct mount *, int *, struct thread *); int softdep_flushfiles(struct mount *, int, struct thread *); void softdep_update_inodeblock(struct inode *, struct buf *, int); void softdep_load_inodeblock(struct inode *); void softdep_freefile(struct vnode *, ino_t, int); int softdep_request_cleanup(struct fs *, struct vnode *, struct ucred *, int); int softdep_prerename(struct vnode *, struct vnode *, struct vnode *, struct vnode *); int softdep_prelink(struct vnode *, struct vnode *, struct componentname *); void softdep_setup_freeblocks(struct inode *, off_t, int); void softdep_setup_inomapdep(struct buf *, struct inode *, ino_t, int); void softdep_setup_blkmapdep(struct buf *, struct mount *, ufs2_daddr_t, int, int); void softdep_setup_allocdirect(struct inode *, ufs_lbn_t, ufs2_daddr_t, ufs2_daddr_t, long, long, struct buf *); void softdep_setup_allocext(struct inode *, ufs_lbn_t, ufs2_daddr_t, ufs2_daddr_t, long, long, struct buf *); void softdep_setup_allocindir_meta(struct buf *, struct inode *, struct buf *, int, ufs2_daddr_t); void softdep_setup_allocindir_page(struct inode *, ufs_lbn_t, struct buf *, int, ufs2_daddr_t, ufs2_daddr_t, struct buf *); void softdep_setup_blkfree(struct mount *, struct buf *, ufs2_daddr_t, int, struct workhead *); void softdep_setup_inofree(struct mount *, struct buf *, ino_t, struct workhead *); void softdep_setup_sbupdate(struct ufsmount *, struct fs *, struct buf *); void softdep_fsync_mountdev(struct vnode *); int softdep_sync_metadata(struct vnode *); int softdep_sync_buf(struct vnode *, struct buf *, int); int softdep_fsync(struct vnode *); int softdep_prealloc(struct vnode *, int); int softdep_journal_lookup(struct mount *, struct vnode **); void softdep_journal_freeblocks(struct inode *, struct ucred *, off_t, int); void softdep_journal_fsync(struct inode *); void softdep_buf_append(struct buf *, struct workhead *); void softdep_inode_append(struct inode *, struct ucred *, struct workhead *); void softdep_freework(struct workhead *); /* * Things to request flushing in softdep_request_cleanup() */ #define FLUSH_INODES 1 #define FLUSH_INODES_WAIT 2 #define FLUSH_BLOCKS 3 #define FLUSH_BLOCKS_WAIT 4 /* * Flag to ffs_syncvnode() to request flushing of data only, * but skip the ffs_update() on the inode itself. Used to avoid * deadlock when flushing snapshot inodes while holding snaplk. */ #define NO_INO_UPDT 0x00000001 /* * Request data sync only from ffs_syncvnode(), not touching even more * metadata than NO_INO_UPDT. */ #define DATA_ONLY 0x00000002 int ffs_rdonly(struct inode *); TAILQ_HEAD(snaphead, inode); struct snapdata { LIST_ENTRY(snapdata) sn_link; struct snaphead sn_head; daddr_t sn_listsize; daddr_t *sn_blklist; struct lock sn_lock; }; #endif /* _KERNEL */ #endif /* !_UFS_FFS_EXTERN_H */ diff --git a/sys/ufs/ffs/ffs_subr.c b/sys/ufs/ffs/ffs_subr.c index 502ef79c7bd3..611f1989e8f9 100644 --- a/sys/ufs/ffs/ffs_subr.c +++ b/sys/ufs/ffs/ffs_subr.c @@ -1,817 +1,834 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * 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 */ #include __FBSDID("$FreeBSD$"); #include #include #ifndef _KERNEL #include #include #include #include #include #include #include uint32_t calculate_crc32c(uint32_t, const void *, size_t); uint32_t ffs_calc_sbhash(struct fs *); struct malloc_type; #define UFS_MALLOC(size, type, flags) malloc(size) #define UFS_FREE(ptr, type) free(ptr) #define maxphys MAXPHYS #else /* _KERNEL */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define UFS_MALLOC(size, type, flags) malloc(size, type, flags) #define UFS_FREE(ptr, type) free(ptr, type) #endif /* _KERNEL */ /* * Verify an inode check-hash. */ int ffs_verify_dinode_ckhash(struct fs *fs, struct ufs2_dinode *dip) { uint32_t ckhash, save_ckhash; /* * Return success if unallocated or we are not doing inode check-hash. */ if (dip->di_mode == 0 || (fs->fs_metackhash & CK_INODE) == 0) return (0); /* * Exclude di_ckhash from the crc32 calculation, e.g., always use * a check-hash value of zero when calculating the check-hash. */ save_ckhash = dip->di_ckhash; dip->di_ckhash = 0; ckhash = calculate_crc32c(~0L, (void *)dip, sizeof(*dip)); dip->di_ckhash = save_ckhash; if (save_ckhash == ckhash) return (0); return (EINVAL); } /* * Update an inode check-hash. */ void ffs_update_dinode_ckhash(struct fs *fs, struct ufs2_dinode *dip) { if (dip->di_mode == 0 || (fs->fs_metackhash & CK_INODE) == 0) return; /* * Exclude old di_ckhash from the crc32 calculation, e.g., always use * a check-hash value of zero when calculating the new check-hash. */ dip->di_ckhash = 0; dip->di_ckhash = calculate_crc32c(~0L, (void *)dip, sizeof(*dip)); } /* * These are the low-level functions that actually read and write * the superblock and its associated data. */ static off_t sblock_try[] = SBLOCKSEARCH; -static int readsuper(void *, struct fs **, off_t, int, int, +static int readsuper(void *, struct fs **, off_t, int, int (*)(void *, off_t, void **, int)); static int validate_sblock(struct fs *, int); /* * Read a superblock from the devfd device. * * If an alternate superblock is specified, it is read. Otherwise the * set of locations given in the SBLOCKSEARCH list is searched for a * superblock. Memory is allocated for the superblock by the readfunc and * is returned. If filltype is non-NULL, additional memory is allocated * of type filltype and filled in with the superblock summary information. * All memory is freed when any error is returned. * * If a superblock is found, zero is returned. Otherwise one of the * following error values is returned: * EIO: non-existent or truncated superblock. * EIO: error reading summary information. * ENOENT: no usable known superblock found. * ENOMEM: failed to allocate space for the superblock. * EINVAL: The previous newfs operation on this volume did not complete. * The administrator must complete newfs before using this volume. */ int -ffs_sbget(void *devfd, struct fs **fsp, off_t altsblock, +ffs_sbget(void *devfd, struct fs **fsp, off_t sblock, int flags, struct malloc_type *filltype, int (*readfunc)(void *devfd, off_t loc, void **bufp, int size)) { struct fs *fs; struct fs_summary_info *fs_si; int i, error; uint64_t size, blks; uint8_t *space; int32_t *lp; char *buf; fs = NULL; *fsp = NULL; - if (altsblock >= 0) { - if ((error = readsuper(devfd, &fs, altsblock, 1, 0, - readfunc)) != 0) { + if (sblock != UFS_STDSB) { + if ((error = readsuper(devfd, &fs, sblock, + flags | UFS_ALTSBLK, readfunc)) != 0) { if (fs != NULL) UFS_FREE(fs, filltype); return (error); } } else { for (i = 0; sblock_try[i] != -1; i++) { - if ((error = readsuper(devfd, &fs, sblock_try[i], 0, - altsblock, readfunc)) == 0) + if ((error = readsuper(devfd, &fs, sblock_try[i], + flags, readfunc)) == 0) { + if ((flags & UFS_NOCSUM) != 0) { + *fsp = fs; + return (0); + } break; + } if (fs != NULL) { UFS_FREE(fs, filltype); fs = NULL; } if (error == ENOENT) continue; return (error); } if (sblock_try[i] == -1) return (ENOENT); } /* * Read in the superblock summary information. */ size = fs->fs_cssize; blks = howmany(size, fs->fs_fsize); if (fs->fs_contigsumsize > 0) size += fs->fs_ncg * sizeof(int32_t); size += fs->fs_ncg * sizeof(u_int8_t); if ((fs_si = UFS_MALLOC(sizeof(*fs_si), filltype, M_NOWAIT)) == NULL) { UFS_FREE(fs, filltype); return (ENOMEM); } bzero(fs_si, sizeof(*fs_si)); fs->fs_si = fs_si; if ((space = UFS_MALLOC(size, filltype, M_NOWAIT)) == NULL) { UFS_FREE(fs->fs_si, filltype); UFS_FREE(fs, filltype); return (ENOMEM); } 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; buf = NULL; error = (*readfunc)(devfd, dbtob(fsbtodb(fs, fs->fs_csaddr + i)), (void **)&buf, size); if (error) { if (buf != NULL) UFS_FREE(buf, filltype); UFS_FREE(fs->fs_csp, filltype); UFS_FREE(fs->fs_si, filltype); UFS_FREE(fs, filltype); return (error); } memcpy(space, buf, size); UFS_FREE(buf, filltype); space += size; } if (fs->fs_contigsumsize > 0) { fs->fs_maxcluster = lp = (int32_t *)space; for (i = 0; i < fs->fs_ncg; i++) *lp++ = fs->fs_contigsumsize; space = (uint8_t *)lp; } size = fs->fs_ncg * sizeof(u_int8_t); fs->fs_contigdirs = (u_int8_t *)space; bzero(fs->fs_contigdirs, size); *fsp = fs; return (0); } /* * Try to read a superblock from the location specified by sblockloc. * Return zero on success or an errno on failure. */ static int -readsuper(void *devfd, struct fs **fsp, off_t sblockloc, int isaltsblk, - int chkhash, int (*readfunc)(void *devfd, off_t loc, void **bufp, int size)) +readsuper(void *devfd, struct fs **fsp, off_t sblockloc, int flags, + int (*readfunc)(void *devfd, off_t loc, void **bufp, int size)) { struct fs *fs; int error, res; uint32_t ckhash; error = (*readfunc)(devfd, sblockloc, (void **)fsp, SBLOCKSIZE); if (error != 0) return (error); fs = *fsp; if (fs->fs_magic == FS_BAD_MAGIC) return (EINVAL); /* * For UFS1 with a 65536 block size, the first backup superblock * is at the same location as the UFS2 superblock. Since SBLOCK_UFS2 * is the first location checked, the first backup is the superblock * that will be accessed. Here we fail the lookup so that we can * retry with the correct location for the UFS1 superblock. */ - if (fs->fs_magic == FS_UFS1_MAGIC && !isaltsblk && + if (fs->fs_magic == FS_UFS1_MAGIC && (flags & UFS_ALTSBLK) == 0 && fs->fs_bsize == SBLOCK_UFS2 && sblockloc == SBLOCK_UFS2) return (ENOENT); - if ((error = validate_sblock(fs, isaltsblk)) != 0) + if ((error = validate_sblock(fs, flags)) > 0) return (error); /* * If the filesystem has been run on a kernel without * metadata check hashes, disable them. */ if ((fs->fs_flags & FS_METACKHASH) == 0) fs->fs_metackhash = 0; /* * Clear any check-hashes that are not maintained * by this kernel. Also clear any unsupported flags. */ fs->fs_metackhash &= CK_SUPPORTED; fs->fs_flags &= FS_SUPPORTED; if (fs->fs_ckhash != (ckhash = ffs_calc_sbhash(fs))) { - if (chkhash == STDSB_NOMSG) - return (EINTEGRITY); - if (chkhash == STDSB_NOHASHFAIL_NOMSG) + if ((flags & (UFS_NOMSG | UFS_NOHASHFAIL)) == + (UFS_NOMSG | UFS_NOHASHFAIL)) return (0); + if ((flags & UFS_NOMSG) != 0) + return (EINTEGRITY); #ifdef _KERNEL res = uprintf("Superblock check-hash failed: recorded " "check-hash 0x%x != computed check-hash 0x%x%s\n", fs->fs_ckhash, ckhash, - chkhash == STDSB_NOHASHFAIL ? " (Ignored)" : ""); + (flags & UFS_NOHASHFAIL) != 0 ? " (Ignored)" : ""); #else res = 0; #endif /* * Print check-hash failure if no controlling terminal * in kernel or always if in user-mode (libufs). */ if (res == 0) printf("Superblock check-hash failed: recorded " "check-hash 0x%x != computed check-hash " "0x%x%s\n", fs->fs_ckhash, ckhash, - chkhash == STDSB_NOHASHFAIL ? - " (Ignored)" : ""); - if (chkhash == STDSB) - return (EINTEGRITY); - /* chkhash == STDSB_NOHASHFAIL */ - return (0); + (flags & UFS_NOHASHFAIL) ? " (Ignored)" : ""); + if ((flags & UFS_NOHASHFAIL) != 0) + return (0); + return (EINTEGRITY); } /* Have to set for old filesystems that predate this field */ fs->fs_sblockactualloc = sblockloc; /* Not yet any summary information */ fs->fs_si = NULL; return (0); } /* * Verify the filesystem values. */ -#define ILOG2(num) (fls(num) - 1) +#define ILOG2(num) (fls(num) - 1) +#define MPRINT if (prtmsg) printf #undef CHK #define CHK(lhs, op, rhs, fmt) \ if (lhs op rhs) { \ - printf("UFS%d superblock failed: %s (" #fmt ") %s %s (" \ + MPRINT("UFS%d superblock failed: %s (" #fmt ") %s %s (" \ #fmt ")\n", fs->fs_magic == FS_UFS1_MAGIC ? 1 : 2, \ #lhs, (intmax_t)lhs, #op, #rhs, (intmax_t)rhs); \ - return (ENOENT); \ + if (error == 0) \ + error = ENOENT; \ } #define CHK2(lhs1, op1, rhs1, lhs2, op2, rhs2, fmt) \ if (lhs1 op1 rhs1 && lhs2 op2 rhs2) { \ - printf("UFS%d superblock failed: %s (" #fmt ") %s %s (" \ + MPRINT("UFS%d superblock failed: %s (" #fmt ") %s %s (" \ #fmt ") && %s (" #fmt ") %s %s (" #fmt ")\n", \ fs->fs_magic == FS_UFS1_MAGIC ? 1 : 2, #lhs1, \ (intmax_t)lhs1, #op1, #rhs1, (intmax_t)rhs1, #lhs2, \ (intmax_t)lhs2, #op2, #rhs2, (intmax_t)rhs2); \ - return (ENOENT); \ + if (error == 0) \ + error = ENOENT; \ } static int -validate_sblock(struct fs *fs, int isaltsblk) +validate_sblock(struct fs *fs, int flags) { u_long i, sectorsize; u_int64_t maxfilesize, sizepb; + int error, prtmsg; + error = 0; sectorsize = dbtob(1); + prtmsg = ((flags & UFS_NOMSG) == 0); if (fs->fs_magic == FS_UFS2_MAGIC) { - if (!isaltsblk) { + if ((flags & UFS_ALTSBLK) == 0) { CHK(fs->fs_sblockloc, !=, SBLOCK_UFS2, %#jx); CHK2(fs->fs_sblockactualloc, !=, SBLOCK_UFS2, fs->fs_sblockactualloc, !=, 0, %jd); } CHK(fs->fs_maxsymlinklen, !=, ((UFS_NDADDR + UFS_NIADDR) * sizeof(ufs2_daddr_t)), %jd); CHK(fs->fs_nindir, !=, fs->fs_bsize / sizeof(ufs2_daddr_t), %jd); CHK(fs->fs_inopb, <, 1, %jd); CHK(fs->fs_inopb, !=, fs->fs_bsize / sizeof(struct ufs2_dinode), %jd); } else if (fs->fs_magic == FS_UFS1_MAGIC) { - if (!isaltsblk) { + if ((flags & UFS_ALTSBLK) == 0) { CHK(fs->fs_sblockloc, >, SBLOCK_UFS1, %jd); CHK(fs->fs_sblockactualloc, >, SBLOCK_UFS1, %jd); } CHK(fs->fs_nindir, !=, fs->fs_bsize / sizeof(ufs1_daddr_t), %jd); CHK(fs->fs_inopb, <, 1, %jd); CHK(fs->fs_inopb, !=, fs->fs_bsize / sizeof(struct ufs1_dinode), %jd); CHK(fs->fs_maxsymlinklen, !=, ((UFS_NDADDR + UFS_NIADDR) * sizeof(ufs1_daddr_t)), %jd); CHK(fs->fs_old_inodefmt, !=, FS_44INODEFMT, %jd); CHK(fs->fs_old_cgoffset, !=, 0, %jd); CHK(fs->fs_old_cgmask, !=, 0xffffffff, %#jx); CHK(fs->fs_old_rotdelay, !=, 0, %jd); CHK(fs->fs_old_rps, !=, 60, %jd); CHK(fs->fs_old_nspf, !=, fs->fs_fsize / sectorsize, %jd); CHK(fs->fs_old_cpg, !=, 1, %jd); CHK(fs->fs_old_interleave, !=, 1, %jd); CHK(fs->fs_old_trackskew, !=, 0, %jd); CHK(fs->fs_old_cpc, !=, 0, %jd); CHK(fs->fs_old_postblformat, !=, 1, %jd); CHK(fs->fs_old_nrpos, !=, 1, %jd); CHK(fs->fs_old_spc, !=, fs->fs_fpg * fs->fs_old_nspf, %jd); CHK(fs->fs_old_nsect, !=, fs->fs_old_spc, %jd); CHK(fs->fs_old_npsect, !=, fs->fs_old_spc, %jd); CHK(fs->fs_old_ncyl, !=, fs->fs_ncg, %jd); } else { /* Bad magic number, so assume not a superblock */ return (ENOENT); } CHK(fs->fs_bsize, <, MINBSIZE, %jd); CHK(fs->fs_bsize, >, MAXBSIZE, %jd); CHK(fs->fs_bsize, <, roundup(sizeof(struct fs), DEV_BSIZE), %jd); CHK(powerof2(fs->fs_bsize), ==, 0, %jd); CHK(fs->fs_frag, <, 1, %jd); CHK(fs->fs_frag, >, MAXFRAG, %jd); CHK(fs->fs_frag, !=, numfrags(fs, fs->fs_bsize), %jd); CHK(fs->fs_fsize, <, sectorsize, %jd); CHK(fs->fs_fsize * fs->fs_frag, !=, fs->fs_bsize, %jd); CHK(powerof2(fs->fs_fsize), ==, 0, %jd); CHK(fs->fs_fpg, <, 3 * fs->fs_frag, %jd); CHK(fs->fs_ncg, <, 1, %jd); CHK(fs->fs_ipg, <, 1, %jd); CHK(fs->fs_ipg * fs->fs_ncg, >, (((int64_t)(1)) << 32) - INOPB(fs), %jd); CHK(fs->fs_sblockloc, <, 0, %jd); CHK(fs->fs_sblockloc, >, fs->fs_fpg, %jd); CHK(fs->fs_sbsize, >, SBLOCKSIZE, %jd); CHK(fs->fs_maxbsize, <, fs->fs_bsize, %jd); CHK(powerof2(fs->fs_maxbsize), ==, 0, %jd); CHK(fs->fs_maxbsize, >, FS_MAXCONTIG * fs->fs_bsize, %jd); CHK(fs->fs_bmask, !=, ~(fs->fs_bsize - 1), %#jx); CHK(fs->fs_fmask, !=, ~(fs->fs_fsize - 1), %#jx); CHK(fs->fs_qbmask, !=, ~fs->fs_bmask, %#jx); CHK(fs->fs_qfmask, !=, ~fs->fs_fmask, %#jx); CHK(fs->fs_bshift, !=, ILOG2(fs->fs_bsize), %jd); CHK(fs->fs_fshift, !=, ILOG2(fs->fs_fsize), %jd); CHK(fs->fs_fragshift, !=, ILOG2(fs->fs_frag), %jd); CHK(fs->fs_fsbtodb, !=, ILOG2(fs->fs_fsize / sectorsize), %jd); CHK(fs->fs_sblkno, !=, roundup( howmany(fs->fs_sblockloc + SBLOCKSIZE, fs->fs_fsize), fs->fs_frag), %jd); CHK(fs->fs_cblkno, !=, fs->fs_sblkno + roundup(howmany(SBLOCKSIZE, fs->fs_fsize), fs->fs_frag), %jd); CHK(fs->fs_iblkno, !=, fs->fs_cblkno + fs->fs_frag, %jd); CHK(fs->fs_dblkno, !=, fs->fs_iblkno + fs->fs_ipg / INOPF(fs), %jd); CHK(fs->fs_cgsize, >, fs->fs_bsize, %jd); /* * This test is valid, however older versions of growfs failed * to correctly update fs_dsize so will fail this test. Thus we * exclude it from the requirements. */ #ifdef notdef CHK(fs->fs_dsize, !=, fs->fs_size - fs->fs_sblkno - fs->fs_ncg * (fs->fs_dblkno - fs->fs_sblkno) - howmany(fs->fs_cssize, fs->fs_fsize), %jd); #endif CHK(fs->fs_metaspace, <, 0, %jd); CHK(fs->fs_metaspace, >, fs->fs_fpg / 2, %jd); CHK(fs->fs_minfree, >, 99, %jd%%); maxfilesize = fs->fs_bsize * UFS_NDADDR - 1; for (sizepb = fs->fs_bsize, i = 0; i < UFS_NIADDR; i++) { sizepb *= NINDIR(fs); maxfilesize += sizepb; } CHK(fs->fs_maxfilesize, !=, maxfilesize, %jd); /* * These values have a tight interaction with each other that * makes it hard to tightly bound them. So we can only check * that they are within a broader possible range. * * The size cannot always be accurately determined, but ensure * that it is consistent with the number of cylinder groups (fs_ncg) * and the number of fragments per cylinder group (fs_fpg). Ensure * that the summary information size is correct and that it starts * and ends in the data area of the same cylinder group. */ CHK(fs->fs_size, <, 8 * fs->fs_frag, %jd); CHK(fs->fs_size, <=, (fs->fs_ncg - 1) * fs->fs_fpg, %jd); CHK(fs->fs_size, >, fs->fs_ncg * fs->fs_fpg, %jd); + /* + * If we are not requested to read in the csum data stop here + * as the correctness of the remaining values is only important + * to bound the space needed to be allocated to hold the csum data. + */ + if ((flags & UFS_NOCSUM) != 0) + return (error); CHK(fs->fs_csaddr, <, 0, %jd); CHK(fs->fs_cssize, !=, fragroundup(fs, fs->fs_ncg * sizeof(struct csum)), %jd); CHK(dtog(fs, fs->fs_csaddr), >, fs->fs_ncg, %jd); CHK(fs->fs_csaddr, <, cgdmin(fs, dtog(fs, fs->fs_csaddr)), %jd); CHK(dtog(fs, fs->fs_csaddr + howmany(fs->fs_cssize, fs->fs_fsize)), >, dtog(fs, fs->fs_csaddr), %jd); /* * With file system clustering it is possible to allocate * many contiguous blocks. The kernel variable maxphys defines * the maximum transfer size permitted by the controller and/or * buffering. The fs_maxcontig parameter controls the maximum * number of blocks that the filesystem will read or write * in a single transfer. It is calculated when the filesystem * is created as maxphys / fs_bsize. The loader uses a maxphys * of 128K even when running on a system that supports larger * values. If the filesystem was built on a system that supports * a larger maxphys (1M is typical) it will have configured * fs_maxcontig for that larger system. So we bound the upper * allowable limit for fs_maxconfig to be able to at least * work with a 1M maxphys on the smallest block size filesystem: * 1M / 4096 == 256. There is no harm in allowing the mounting of * filesystems that make larger than maxphys I/O requests because * those (mostly 32-bit machines) can (very slowly) handle I/O * requests that exceed maxphys. */ CHK(fs->fs_maxcontig, <, 0, %jd); CHK(fs->fs_maxcontig, >, MAX(256, maxphys / fs->fs_bsize), %jd); CHK2(fs->fs_maxcontig, ==, 0, fs->fs_contigsumsize, !=, 0, %jd); CHK2(fs->fs_maxcontig, >, 1, fs->fs_contigsumsize, !=, MIN(fs->fs_maxcontig, FS_MAXCONTIG), %jd); - return (0); + return (error); } /* * Write a superblock to the devfd device from the memory pointed to by fs. * Write out the superblock summary information if it is present. * * If the write is successful, zero is returned. Otherwise one of the * following error values is returned: * EIO: failed to write superblock. * EIO: failed to write superblock summary information. */ int ffs_sbput(void *devfd, struct fs *fs, off_t loc, int (*writefunc)(void *devfd, off_t loc, void *buf, int size)) { int i, error, blks, size; uint8_t *space; /* * If there is summary information, write it first, so if there * is an error, the superblock will not be marked as clean. */ if (fs->fs_si != NULL && fs->fs_csp != NULL) { blks = howmany(fs->fs_cssize, fs->fs_fsize); space = (uint8_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; if ((error = (*writefunc)(devfd, dbtob(fsbtodb(fs, fs->fs_csaddr + i)), space, size)) != 0) return (error); space += size; } } fs->fs_fmod = 0; #ifndef _KERNEL { struct fs_summary_info *fs_si; fs->fs_time = time(NULL); /* Clear the pointers for the duration of writing. */ fs_si = fs->fs_si; fs->fs_si = NULL; fs->fs_ckhash = ffs_calc_sbhash(fs); error = (*writefunc)(devfd, loc, fs, fs->fs_sbsize); fs->fs_si = fs_si; } #else /* _KERNEL */ fs->fs_time = time_second; fs->fs_ckhash = ffs_calc_sbhash(fs); error = (*writefunc)(devfd, loc, fs, fs->fs_sbsize); #endif /* _KERNEL */ return (error); } /* * Calculate the check-hash for a superblock. */ uint32_t ffs_calc_sbhash(struct fs *fs) { uint32_t ckhash, save_ckhash; /* * A filesystem that was using a superblock ckhash may be moved * to an older kernel that does not support ckhashes. The * older kernel will clear the FS_METACKHASH flag indicating * that it does not update hashes. When the disk is moved back * to a kernel capable of ckhashes it disables them on mount: * * if ((fs->fs_flags & FS_METACKHASH) == 0) * fs->fs_metackhash = 0; * * This leaves (fs->fs_metackhash & CK_SUPERBLOCK) == 0) with an * old stale value in the fs->fs_ckhash field. Thus the need to * just accept what is there. */ if ((fs->fs_metackhash & CK_SUPERBLOCK) == 0) return (fs->fs_ckhash); save_ckhash = fs->fs_ckhash; fs->fs_ckhash = 0; /* * If newly read from disk, the caller is responsible for * verifying that fs->fs_sbsize <= SBLOCKSIZE. */ ckhash = calculate_crc32c(~0L, (void *)fs, fs->fs_sbsize); fs->fs_ckhash = save_ckhash; return (ckhash); } /* * 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 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)))) == 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; } } } /* * block operations * * check if a block is available */ int ffs_isblock(struct fs *fs, unsigned char *cp, ufs1_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: #ifdef _KERNEL panic("ffs_isblock"); #endif break; } return (0); } /* * check if a block is free */ int ffs_isfreeblock(struct fs *fs, u_char *cp, ufs1_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: #ifdef _KERNEL panic("ffs_isfreeblock"); #endif break; } return (0); } /* * take a block out of the map */ void ffs_clrblock(struct fs *fs, u_char *cp, ufs1_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: #ifdef _KERNEL panic("ffs_clrblock"); #endif break; } } /* * put a block into the map */ void ffs_setblock(struct fs *fs, unsigned char *cp, ufs1_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: #ifdef _KERNEL panic("ffs_setblock"); #endif break; } } /* * 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, ufs1_daddr_t blkno, int cnt) { int32_t *sump; int32_t *lp; u_char *freemapp, *mapp; int i, start, end, forw, back, map; u_int 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 = 1U << (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 = 1U << (start % NBBY); for (i = start; i > end; i--) { if ((map & bit) == 0) break; if ((i & (NBBY - 1)) != 0) { bit >>= 1; } else { map = *mapp--; bit = 1U << (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; } diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c index b41c162dea7d..2944d0c5077d 100644 --- a/sys/ufs/ffs/ffs_vfsops.c +++ b/sys/ufs/ffs/ffs_vfsops.c @@ -1,2600 +1,2602 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * 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.31 (Berkeley) 5/20/95 */ #include __FBSDID("$FreeBSD$"); #include "opt_quota.h" #include "opt_ufs.h" #include "opt_ffs.h" #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static uma_zone_t uma_inode, uma_ufs1, uma_ufs2; VFS_SMR_DECLARE; static int ffs_mountfs(struct vnode *, struct mount *, struct thread *); static void ffs_oldfscompat_read(struct fs *, struct ufsmount *, ufs2_daddr_t); static void ffs_ifree(struct ufsmount *ump, struct inode *ip); static int ffs_sync_lazy(struct mount *mp); static int ffs_use_bread(void *devfd, off_t loc, void **bufp, int size); static int ffs_use_bwrite(void *devfd, off_t loc, void *buf, int size); static vfs_init_t ffs_init; static vfs_uninit_t ffs_uninit; static vfs_extattrctl_t ffs_extattrctl; static vfs_cmount_t ffs_cmount; static vfs_unmount_t ffs_unmount; static vfs_mount_t ffs_mount; static vfs_statfs_t ffs_statfs; static vfs_fhtovp_t ffs_fhtovp; static vfs_sync_t ffs_sync; static struct vfsops ufs_vfsops = { .vfs_extattrctl = ffs_extattrctl, .vfs_fhtovp = ffs_fhtovp, .vfs_init = ffs_init, .vfs_mount = ffs_mount, .vfs_cmount = ffs_cmount, .vfs_quotactl = ufs_quotactl, .vfs_root = vfs_cache_root, .vfs_cachedroot = ufs_root, .vfs_statfs = ffs_statfs, .vfs_sync = ffs_sync, .vfs_uninit = ffs_uninit, .vfs_unmount = ffs_unmount, .vfs_vget = ffs_vget, .vfs_susp_clean = process_deferred_inactive, }; VFS_SET(ufs_vfsops, ufs, 0); MODULE_VERSION(ufs, 1); static b_strategy_t ffs_geom_strategy; static b_write_t ffs_bufwrite; static struct buf_ops ffs_ops = { .bop_name = "FFS", .bop_write = ffs_bufwrite, .bop_strategy = ffs_geom_strategy, .bop_sync = bufsync, #ifdef NO_FFS_SNAPSHOT .bop_bdflush = bufbdflush, #else .bop_bdflush = ffs_bdflush, #endif }; /* * Note that userquota and groupquota options are not currently used * by UFS/FFS code and generally mount(8) does not pass those options * from userland, but they can be passed by loader(8) via * vfs.root.mountfrom.options. */ static const char *ffs_opts[] = { "acls", "async", "noatime", "noclusterr", "noclusterw", "noexec", "export", "force", "from", "groupquota", "multilabel", "nfsv4acls", "snapshot", "nosuid", "suiddir", "nosymfollow", "sync", "union", "userquota", "untrusted", NULL }; static int ffs_enxio_enable = 1; SYSCTL_DECL(_vfs_ffs); SYSCTL_INT(_vfs_ffs, OID_AUTO, enxio_enable, CTLFLAG_RWTUN, &ffs_enxio_enable, 0, "enable mapping of other disk I/O errors to ENXIO"); /* * 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. */ static int ffs_blkatoff(struct vnode *vp, off_t offset, char **res, struct buf **bpp) { struct inode *ip; struct fs *fs; struct buf *bp; ufs_lbn_t lbn; int bsize, error; ip = VTOI(vp); fs = ITOFS(ip); lbn = lblkno(fs, offset); bsize = blksize(fs, ip, lbn); *bpp = NULL; error = bread(vp, lbn, bsize, NOCRED, &bp); if (error) { return (error); } if (res) *res = (char *)bp->b_data + blkoff(fs, offset); *bpp = bp; return (0); } /* * Load up the contents of an inode and copy the appropriate pieces * to the incore copy. */ static int ffs_load_inode(struct buf *bp, struct inode *ip, struct fs *fs, ino_t ino) { struct ufs1_dinode *dip1; struct ufs2_dinode *dip2; int error; if (I_IS_UFS1(ip)) { dip1 = ip->i_din1; *dip1 = *((struct ufs1_dinode *)bp->b_data + ino_to_fsbo(fs, ino)); ip->i_mode = dip1->di_mode; ip->i_nlink = dip1->di_nlink; ip->i_effnlink = dip1->di_nlink; ip->i_size = dip1->di_size; ip->i_flags = dip1->di_flags; ip->i_gen = dip1->di_gen; ip->i_uid = dip1->di_uid; ip->i_gid = dip1->di_gid; return (0); } dip2 = ((struct ufs2_dinode *)bp->b_data + ino_to_fsbo(fs, ino)); if ((error = ffs_verify_dinode_ckhash(fs, dip2)) != 0 && !ffs_fsfail_cleanup(ITOUMP(ip), error)) { printf("%s: inode %jd: check-hash failed\n", fs->fs_fsmnt, (intmax_t)ino); return (error); } *ip->i_din2 = *dip2; dip2 = ip->i_din2; ip->i_mode = dip2->di_mode; ip->i_nlink = dip2->di_nlink; ip->i_effnlink = dip2->di_nlink; ip->i_size = dip2->di_size; ip->i_flags = dip2->di_flags; ip->i_gen = dip2->di_gen; ip->i_uid = dip2->di_uid; ip->i_gid = dip2->di_gid; return (0); } /* * Verify that a filesystem block number is a valid data block. * This routine is only called on untrusted filesystems. */ static int ffs_check_blkno(struct mount *mp, ino_t inum, ufs2_daddr_t daddr, int blksize) { struct fs *fs; struct ufsmount *ump; ufs2_daddr_t end_daddr; int cg, havemtx; KASSERT((mp->mnt_flag & MNT_UNTRUSTED) != 0, ("ffs_check_blkno called on a trusted file system")); ump = VFSTOUFS(mp); fs = ump->um_fs; cg = dtog(fs, daddr); end_daddr = daddr + numfrags(fs, blksize); /* * Verify that the block number is a valid data block. Also check * that it does not point to an inode block or a superblock. Accept * blocks that are unalloacted (0) or part of snapshot metadata * (BLK_NOCOPY or BLK_SNAP). * * Thus, the block must be in a valid range for the filesystem and * either in the space before a backup superblock (except the first * cylinder group where that space is used by the bootstrap code) or * after the inode blocks and before the end of the cylinder group. */ if ((uint64_t)daddr <= BLK_SNAP || ((uint64_t)end_daddr <= fs->fs_size && ((cg > 0 && end_daddr <= cgsblock(fs, cg)) || (daddr >= cgdmin(fs, cg) && end_daddr <= cgbase(fs, cg) + fs->fs_fpg)))) return (0); if ((havemtx = mtx_owned(UFS_MTX(ump))) == 0) UFS_LOCK(ump); if (ppsratecheck(&ump->um_last_integritymsg, &ump->um_secs_integritymsg, 1)) { UFS_UNLOCK(ump); uprintf("\n%s: inode %jd, out-of-range indirect block " "number %jd\n", mp->mnt_stat.f_mntonname, inum, daddr); if (havemtx) UFS_LOCK(ump); } else if (!havemtx) UFS_UNLOCK(ump); return (EINTEGRITY); } /* * On first ENXIO error, initiate an asynchronous forcible unmount. * Used to unmount filesystems whose underlying media has gone away. * * Return true if a cleanup is in progress. */ int ffs_fsfail_cleanup(struct ufsmount *ump, int error) { int retval; UFS_LOCK(ump); retval = ffs_fsfail_cleanup_locked(ump, error); UFS_UNLOCK(ump); return (retval); } int ffs_fsfail_cleanup_locked(struct ufsmount *ump, int error) { mtx_assert(UFS_MTX(ump), MA_OWNED); if (error == ENXIO && (ump->um_flags & UM_FSFAIL_CLEANUP) == 0) { ump->um_flags |= UM_FSFAIL_CLEANUP; /* * Queue an async forced unmount. */ vfs_ref(ump->um_mountp); dounmount(ump->um_mountp, MNT_FORCE | MNT_RECURSE | MNT_DEFERRED, curthread); printf("UFS: forcibly unmounting %s from %s\n", ump->um_mountp->mnt_stat.f_mntfromname, ump->um_mountp->mnt_stat.f_mntonname); } return ((ump->um_flags & UM_FSFAIL_CLEANUP) != 0); } /* * Wrapper used during ENXIO cleanup to allocate empty buffers when * the kernel is unable to read the real one. They are needed so that * the soft updates code can use them to unwind its dependencies. */ int ffs_breadz(struct ufsmount *ump, struct vnode *vp, daddr_t lblkno, daddr_t dblkno, int size, daddr_t *rablkno, int *rabsize, int cnt, struct ucred *cred, int flags, void (*ckhashfunc)(struct buf *), struct buf **bpp) { int error; flags |= GB_CVTENXIO; error = breadn_flags(vp, lblkno, dblkno, size, rablkno, rabsize, cnt, cred, flags, ckhashfunc, bpp); if (error != 0 && ffs_fsfail_cleanup(ump, error)) { error = getblkx(vp, lblkno, dblkno, size, 0, 0, flags, bpp); KASSERT(error == 0, ("getblkx failed")); vfs_bio_bzero_buf(*bpp, 0, size); } return (error); } static int ffs_mount(struct mount *mp) { struct vnode *devvp, *odevvp; struct thread *td; struct ufsmount *ump = NULL; struct fs *fs; int error, flags; int error1 __diagused; uint64_t mntorflags, saved_mnt_flag; accmode_t accmode; struct nameidata ndp; char *fspec; bool mounted_softdep; td = curthread; if (vfs_filteropt(mp->mnt_optnew, ffs_opts)) return (EINVAL); if (uma_inode == NULL) { uma_inode = uma_zcreate("FFS inode", sizeof(struct inode), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); uma_ufs1 = uma_zcreate("FFS1 dinode", sizeof(struct ufs1_dinode), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); uma_ufs2 = uma_zcreate("FFS2 dinode", sizeof(struct ufs2_dinode), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); VFS_SMR_ZONE_SET(uma_inode); } vfs_deleteopt(mp->mnt_optnew, "groupquota"); vfs_deleteopt(mp->mnt_optnew, "userquota"); fspec = vfs_getopts(mp->mnt_optnew, "from", &error); if (error) return (error); mntorflags = 0; if (vfs_getopt(mp->mnt_optnew, "untrusted", NULL, NULL) == 0) mntorflags |= MNT_UNTRUSTED; if (vfs_getopt(mp->mnt_optnew, "acls", NULL, NULL) == 0) mntorflags |= MNT_ACLS; if (vfs_getopt(mp->mnt_optnew, "snapshot", NULL, NULL) == 0) { mntorflags |= MNT_SNAPSHOT; /* * Once we have set the MNT_SNAPSHOT flag, do not * persist "snapshot" in the options list. */ vfs_deleteopt(mp->mnt_optnew, "snapshot"); vfs_deleteopt(mp->mnt_opt, "snapshot"); } if (vfs_getopt(mp->mnt_optnew, "nfsv4acls", NULL, NULL) == 0) { if (mntorflags & MNT_ACLS) { vfs_mount_error(mp, "\"acls\" and \"nfsv4acls\" options " "are mutually exclusive"); return (EINVAL); } mntorflags |= MNT_NFS4ACLS; } MNT_ILOCK(mp); mp->mnt_kern_flag &= ~MNTK_FPLOOKUP; mp->mnt_flag |= mntorflags; MNT_IUNLOCK(mp); /* * If this is a snapshot request, take the snapshot. */ if (mp->mnt_flag & MNT_SNAPSHOT) return (ffs_snapshot(mp, fspec)); /* * Must not call namei() while owning busy ref. */ if (mp->mnt_flag & MNT_UPDATE) vfs_unbusy(mp); /* * Not an update, or updating the name: look up the name * and verify that it refers to a sensible disk device. */ NDINIT(&ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspec); error = namei(&ndp); if ((mp->mnt_flag & MNT_UPDATE) != 0) { /* * Unmount does not start if MNT_UPDATE is set. Mount * update busies mp before setting MNT_UPDATE. We * must be able to retain our busy ref successfully, * without sleep. */ error1 = vfs_busy(mp, MBF_NOWAIT); MPASS(error1 == 0); } if (error != 0) return (error); NDFREE_PNBUF(&ndp); if (!vn_isdisk_error(ndp.ni_vp, &error)) { vput(ndp.ni_vp); return (error); } /* * If mount by non-root, then verify that user has necessary * permissions on the device. */ accmode = VREAD; if ((mp->mnt_flag & MNT_RDONLY) == 0) accmode |= VWRITE; error = VOP_ACCESS(ndp.ni_vp, accmode, td->td_ucred, td); if (error) error = priv_check(td, PRIV_VFS_MOUNT_PERM); if (error) { vput(ndp.ni_vp); return (error); } /* * New mount * * We need the name for the mount point (also used for * "last mounted on") copied in. If an error occurs, * the mount point is discarded by the upper level code. * Note that vfs_mount_alloc() populates f_mntonname for us. */ if ((mp->mnt_flag & MNT_UPDATE) == 0) { if ((error = ffs_mountfs(ndp.ni_vp, mp, td)) != 0) { vrele(ndp.ni_vp); return (error); } } else { /* * When updating, check whether changing from read-only to * read/write; if there is no device name, that's all we do. */ ump = VFSTOUFS(mp); fs = ump->um_fs; odevvp = ump->um_odevvp; devvp = ump->um_devvp; /* * If it's not the same vnode, or at least the same device * then it's not correct. */ if (ndp.ni_vp->v_rdev != ump->um_odevvp->v_rdev) error = EINVAL; /* needs translation */ vput(ndp.ni_vp); if (error) return (error); if (fs->fs_ronly == 0 && vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0)) { /* * Flush any dirty data and suspend filesystem. */ if ((error = vn_start_write(NULL, &mp, V_WAIT)) != 0) return (error); error = vfs_write_suspend_umnt(mp); if (error != 0) return (error); fs->fs_ronly = 1; if (MOUNTEDSOFTDEP(mp)) { MNT_ILOCK(mp); mp->mnt_flag &= ~MNT_SOFTDEP; MNT_IUNLOCK(mp); mounted_softdep = true; } else mounted_softdep = false; /* * Check for and optionally get rid of files open * for writing. */ flags = WRITECLOSE; if (mp->mnt_flag & MNT_FORCE) flags |= FORCECLOSE; if (mounted_softdep) { error = softdep_flushfiles(mp, flags, td); } else { error = ffs_flushfiles(mp, flags, td); } if (error) { fs->fs_ronly = 0; if (mounted_softdep) { MNT_ILOCK(mp); mp->mnt_flag |= MNT_SOFTDEP; MNT_IUNLOCK(mp); } vfs_write_resume(mp, 0); return (error); } if (fs->fs_pendingblocks != 0 || fs->fs_pendinginodes != 0) { printf("WARNING: %s Update error: blocks %jd " "files %d\n", fs->fs_fsmnt, (intmax_t)fs->fs_pendingblocks, fs->fs_pendinginodes); fs->fs_pendingblocks = 0; fs->fs_pendinginodes = 0; } if ((fs->fs_flags & (FS_UNCLEAN | FS_NEEDSFSCK)) == 0) fs->fs_clean = 1; if ((error = ffs_sbupdate(ump, MNT_WAIT, 0)) != 0) { fs->fs_ronly = 0; fs->fs_clean = 0; if (mounted_softdep) { MNT_ILOCK(mp); mp->mnt_flag |= MNT_SOFTDEP; MNT_IUNLOCK(mp); } vfs_write_resume(mp, 0); return (error); } if (mounted_softdep) softdep_unmount(mp); g_topology_lock(); /* * Drop our write and exclusive access. */ g_access(ump->um_cp, 0, -1, -1); g_topology_unlock(); MNT_ILOCK(mp); mp->mnt_flag |= MNT_RDONLY; MNT_IUNLOCK(mp); /* * Allow the writers to note that filesystem * is ro now. */ vfs_write_resume(mp, 0); } if ((mp->mnt_flag & MNT_RELOAD) && (error = ffs_reload(mp, 0)) != 0) return (error); if (fs->fs_ronly && !vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0)) { /* * If upgrade to read-write by non-root, then verify * that user has necessary permissions on the device. */ vn_lock(odevvp, LK_EXCLUSIVE | LK_RETRY); error = VOP_ACCESS(odevvp, VREAD | VWRITE, td->td_ucred, td); if (error) error = priv_check(td, PRIV_VFS_MOUNT_PERM); VOP_UNLOCK(odevvp); if (error) { return (error); } fs->fs_flags &= ~FS_UNCLEAN; if (fs->fs_clean == 0) { fs->fs_flags |= FS_UNCLEAN; if ((mp->mnt_flag & MNT_FORCE) || ((fs->fs_flags & (FS_SUJ | FS_NEEDSFSCK)) == 0 && (fs->fs_flags & FS_DOSOFTDEP))) { printf("WARNING: %s was not properly " "dismounted\n", fs->fs_fsmnt); } else { vfs_mount_error(mp, "R/W mount of %s denied. %s.%s", fs->fs_fsmnt, "Filesystem is not clean - run fsck", (fs->fs_flags & FS_SUJ) == 0 ? "" : " Forced mount will invalidate" " journal contents"); return (EPERM); } } g_topology_lock(); /* * Request exclusive write access. */ error = g_access(ump->um_cp, 0, 1, 1); g_topology_unlock(); if (error) return (error); if ((error = vn_start_write(NULL, &mp, V_WAIT)) != 0) return (error); error = vfs_write_suspend_umnt(mp); if (error != 0) return (error); fs->fs_ronly = 0; MNT_ILOCK(mp); saved_mnt_flag = MNT_RDONLY; if (MOUNTEDSOFTDEP(mp) && (mp->mnt_flag & MNT_ASYNC) != 0) saved_mnt_flag |= MNT_ASYNC; mp->mnt_flag &= ~saved_mnt_flag; MNT_IUNLOCK(mp); fs->fs_mtime = time_second; /* check to see if we need to start softdep */ if ((fs->fs_flags & FS_DOSOFTDEP) && (error = softdep_mount(devvp, mp, fs, td->td_ucred))){ fs->fs_ronly = 1; MNT_ILOCK(mp); mp->mnt_flag |= saved_mnt_flag; MNT_IUNLOCK(mp); vfs_write_resume(mp, 0); return (error); } fs->fs_clean = 0; if ((error = ffs_sbupdate(ump, MNT_WAIT, 0)) != 0) { fs->fs_ronly = 1; if ((fs->fs_flags & FS_DOSOFTDEP) != 0) softdep_unmount(mp); MNT_ILOCK(mp); mp->mnt_flag |= saved_mnt_flag; MNT_IUNLOCK(mp); vfs_write_resume(mp, 0); return (error); } if (fs->fs_snapinum[0] != 0) ffs_snapshot_mount(mp); vfs_write_resume(mp, 0); } /* * Soft updates is incompatible with "async", * so if we are doing softupdates stop the user * from setting the async flag in an update. * Softdep_mount() clears it in an initial mount * or ro->rw remount. */ if (MOUNTEDSOFTDEP(mp)) { /* XXX: Reset too late ? */ MNT_ILOCK(mp); mp->mnt_flag &= ~MNT_ASYNC; MNT_IUNLOCK(mp); } /* * Keep MNT_ACLS flag if it is stored in superblock. */ if ((fs->fs_flags & FS_ACLS) != 0) { /* XXX: Set too late ? */ MNT_ILOCK(mp); mp->mnt_flag |= MNT_ACLS; MNT_IUNLOCK(mp); } if ((fs->fs_flags & FS_NFS4ACLS) != 0) { /* XXX: Set too late ? */ MNT_ILOCK(mp); mp->mnt_flag |= MNT_NFS4ACLS; MNT_IUNLOCK(mp); } } MNT_ILOCK(mp); /* * This is racy versus lookup, see ufs_fplookup_vexec for details. */ if ((mp->mnt_kern_flag & MNTK_FPLOOKUP) != 0) panic("MNTK_FPLOOKUP set on mount %p when it should not be", mp); if ((mp->mnt_flag & (MNT_ACLS | MNT_NFS4ACLS | MNT_UNION)) == 0) mp->mnt_kern_flag |= MNTK_FPLOOKUP; MNT_IUNLOCK(mp); vfs_mountedfrom(mp, fspec); return (0); } /* * Compatibility with old mount system call. */ static int ffs_cmount(struct mntarg *ma, void *data, uint64_t flags) { struct ufs_args args; int error; if (data == NULL) return (EINVAL); error = copyin(data, &args, sizeof args); if (error) return (error); ma = mount_argsu(ma, "from", args.fspec, MAXPATHLEN); ma = mount_arg(ma, "export", &args.export, sizeof(args.export)); error = kernel_mount(ma, flags); return (error); } /* * Reload all incore data for a filesystem (used after running fsck on * the root filesystem and finding things to fix). If the 'force' flag * is 0, 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) clear MNTK_SUSPEND2 and MNTK_SUSPENDED flags, allowing secondary * writers, if requested. * 6) invalidate all cached file data. * 7) re-read inode data for all active vnodes. */ int ffs_reload(struct mount *mp, int flags) { struct vnode *vp, *mvp, *devvp; struct inode *ip; void *space; struct buf *bp; struct fs *fs, *newfs; struct ufsmount *ump; ufs2_daddr_t sblockloc; int i, blks, error; u_long size; int32_t *lp; ump = VFSTOUFS(mp); MNT_ILOCK(mp); if ((mp->mnt_flag & MNT_RDONLY) == 0 && (flags & FFSR_FORCE) == 0) { MNT_IUNLOCK(mp); return (EINVAL); } MNT_IUNLOCK(mp); /* * Step 1: invalidate all cached meta-data. */ devvp = VFSTOUFS(mp)->um_devvp; vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); if (vinvalbuf(devvp, 0, 0, 0) != 0) panic("ffs_reload: dirty1"); VOP_UNLOCK(devvp); /* * Step 2: re-read superblock from disk. */ fs = VFSTOUFS(mp)->um_fs; if ((error = bread(devvp, btodb(fs->fs_sblockloc), fs->fs_sbsize, NOCRED, &bp)) != 0) return (error); newfs = (struct fs *)bp->b_data; if ((newfs->fs_magic != FS_UFS1_MAGIC && newfs->fs_magic != FS_UFS2_MAGIC) || newfs->fs_bsize > MAXBSIZE || newfs->fs_bsize < sizeof(struct fs)) { brelse(bp); return (EIO); /* XXX needs translation */ } /* * Preserve the summary information, read-only status, and * superblock location by copying these fields into our new * superblock before using it to update the existing superblock. */ newfs->fs_si = fs->fs_si; newfs->fs_ronly = fs->fs_ronly; sblockloc = fs->fs_sblockloc; bcopy(newfs, fs, (u_int)fs->fs_sbsize); brelse(bp); ump->um_bsize = fs->fs_bsize; ump->um_maxsymlinklen = fs->fs_maxsymlinklen; ffs_oldfscompat_read(fs, VFSTOUFS(mp), sblockloc); UFS_LOCK(ump); if (fs->fs_pendingblocks != 0 || fs->fs_pendinginodes != 0) { printf("WARNING: %s: reload pending error: blocks %jd " "files %d\n", fs->fs_fsmnt, (intmax_t)fs->fs_pendingblocks, fs->fs_pendinginodes); fs->fs_pendingblocks = 0; fs->fs_pendinginodes = 0; } UFS_UNLOCK(ump); /* * Step 3: re-read summary information from disk. */ size = fs->fs_cssize; blks = howmany(size, fs->fs_fsize); if (fs->fs_contigsumsize > 0) size += fs->fs_ncg * sizeof(int32_t); size += fs->fs_ncg * sizeof(u_int8_t); free(fs->fs_csp, M_UFSMNT); space = malloc(size, M_UFSMNT, M_WAITOK); fs->fs_csp = 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, NOCRED, &bp); if (error) return (error); bcopy(bp->b_data, space, (u_int)size); space = (char *)space + size; brelse(bp); } /* * We no longer know anything about clusters per cylinder group. */ if (fs->fs_contigsumsize > 0) { fs->fs_maxcluster = lp = space; for (i = 0; i < fs->fs_ncg; i++) *lp++ = fs->fs_contigsumsize; space = lp; } size = fs->fs_ncg * sizeof(u_int8_t); fs->fs_contigdirs = (u_int8_t *)space; bzero(fs->fs_contigdirs, size); if ((flags & FFSR_UNSUSPEND) != 0) { MNT_ILOCK(mp); mp->mnt_kern_flag &= ~(MNTK_SUSPENDED | MNTK_SUSPEND2); wakeup(&mp->mnt_flag); MNT_IUNLOCK(mp); } loop: MNT_VNODE_FOREACH_ALL(vp, mp, mvp) { /* * Skip syncer vnode. */ if (vp->v_type == VNON) { VI_UNLOCK(vp); continue; } /* * Step 4: invalidate all cached file data. */ if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK)) { MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp); goto loop; } if (vinvalbuf(vp, 0, 0, 0)) panic("ffs_reload: dirty2"); /* * Step 5: re-read inode data for all active vnodes. */ ip = VTOI(vp); error = bread(devvp, fsbtodb(fs, ino_to_fsba(fs, ip->i_number)), (int)fs->fs_bsize, NOCRED, &bp); if (error) { vput(vp); MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp); return (error); } if ((error = ffs_load_inode(bp, ip, fs, ip->i_number)) != 0) { brelse(bp); vput(vp); MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp); return (error); } ip->i_effnlink = ip->i_nlink; brelse(bp); vput(vp); } return (0); } /* * Common code for mount and mountroot */ static int ffs_mountfs(struct vnode *odevvp, struct mount *mp, struct thread *td) { struct ufsmount *ump; struct fs *fs; struct cdev *dev; int error, i, len, ronly; struct ucred *cred; struct g_consumer *cp; struct mount *nmp; struct vnode *devvp; - int candelete, canspeedup; + int candelete, canspeedup, flags; off_t loc; fs = NULL; ump = NULL; cred = td ? td->td_ucred : NOCRED; ronly = (mp->mnt_flag & MNT_RDONLY) != 0; devvp = mntfs_allocvp(mp, odevvp); VOP_UNLOCK(odevvp); vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); KASSERT(devvp->v_type == VCHR, ("reclaimed devvp")); dev = devvp->v_rdev; KASSERT(dev->si_snapdata == NULL, ("non-NULL snapshot data")); if (atomic_cmpset_acq_ptr((uintptr_t *)&dev->si_mountpt, 0, (uintptr_t)mp) == 0) { mntfs_freevp(devvp); return (EBUSY); } g_topology_lock(); error = g_vfs_open(devvp, &cp, "ffs", ronly ? 0 : 1); g_topology_unlock(); if (error != 0) { atomic_store_rel_ptr((uintptr_t *)&dev->si_mountpt, 0); mntfs_freevp(devvp); return (error); } dev_ref(dev); devvp->v_bufobj.bo_ops = &ffs_ops; BO_LOCK(&odevvp->v_bufobj); odevvp->v_bufobj.bo_flag |= BO_NOBUFS; BO_UNLOCK(&odevvp->v_bufobj); VOP_UNLOCK(devvp); if (dev->si_iosize_max != 0) mp->mnt_iosize_max = dev->si_iosize_max; if (mp->mnt_iosize_max > maxphys) mp->mnt_iosize_max = maxphys; if ((SBLOCKSIZE % cp->provider->sectorsize) != 0) { error = EINVAL; vfs_mount_error(mp, "Invalid sectorsize %d for superblock size %d", cp->provider->sectorsize, SBLOCKSIZE); goto out; } /* fetch the superblock and summary information */ - loc = STDSB; + loc = UFS_STDSB; + flags = 0; if ((mp->mnt_flag & (MNT_ROOTFS | MNT_FORCE)) != 0) - loc = STDSB_NOHASHFAIL; - if ((error = ffs_sbget(devvp, &fs, loc, M_UFSMNT, ffs_use_bread)) != 0) + flags = UFS_NOHASHFAIL; + if ((error = ffs_sbget(devvp, &fs, loc, flags, M_UFSMNT, ffs_use_bread)) + != 0) goto out; fs->fs_flags &= ~FS_UNCLEAN; if (fs->fs_clean == 0) { fs->fs_flags |= FS_UNCLEAN; if (ronly || (mp->mnt_flag & MNT_FORCE) || ((fs->fs_flags & (FS_SUJ | FS_NEEDSFSCK)) == 0 && (fs->fs_flags & FS_DOSOFTDEP))) { printf("WARNING: %s was not properly dismounted\n", fs->fs_fsmnt); } else { vfs_mount_error(mp, "R/W mount of %s denied. %s%s", fs->fs_fsmnt, "Filesystem is not clean - run fsck.", (fs->fs_flags & FS_SUJ) == 0 ? "" : " Forced mount will invalidate journal contents"); error = EPERM; goto out; } if ((fs->fs_pendingblocks != 0 || fs->fs_pendinginodes != 0) && (mp->mnt_flag & MNT_FORCE)) { printf("WARNING: %s: lost blocks %jd files %d\n", fs->fs_fsmnt, (intmax_t)fs->fs_pendingblocks, fs->fs_pendinginodes); fs->fs_pendingblocks = 0; fs->fs_pendinginodes = 0; } } if (fs->fs_pendingblocks != 0 || fs->fs_pendinginodes != 0) { printf("WARNING: %s: mount pending error: blocks %jd " "files %d\n", fs->fs_fsmnt, (intmax_t)fs->fs_pendingblocks, fs->fs_pendinginodes); fs->fs_pendingblocks = 0; fs->fs_pendinginodes = 0; } if ((fs->fs_flags & FS_GJOURNAL) != 0) { #ifdef UFS_GJOURNAL /* * Get journal provider name. */ len = 1024; mp->mnt_gjprovider = malloc((u_long)len, M_UFSMNT, M_WAITOK); if (g_io_getattr("GJOURNAL::provider", cp, &len, mp->mnt_gjprovider) == 0) { mp->mnt_gjprovider = realloc(mp->mnt_gjprovider, len, M_UFSMNT, M_WAITOK); MNT_ILOCK(mp); mp->mnt_flag |= MNT_GJOURNAL; MNT_IUNLOCK(mp); } else { if ((mp->mnt_flag & MNT_RDONLY) == 0) printf("WARNING: %s: GJOURNAL flag on fs " "but no gjournal provider below\n", mp->mnt_stat.f_mntonname); free(mp->mnt_gjprovider, M_UFSMNT); mp->mnt_gjprovider = NULL; } #else printf("WARNING: %s: GJOURNAL flag on fs but no " "UFS_GJOURNAL support\n", mp->mnt_stat.f_mntonname); #endif } else { mp->mnt_gjprovider = NULL; } ump = malloc(sizeof *ump, M_UFSMNT, M_WAITOK | M_ZERO); ump->um_cp = cp; ump->um_bo = &devvp->v_bufobj; ump->um_fs = fs; if (fs->fs_magic == FS_UFS1_MAGIC) { ump->um_fstype = UFS1; ump->um_balloc = ffs_balloc_ufs1; } else { ump->um_fstype = UFS2; ump->um_balloc = ffs_balloc_ufs2; } ump->um_blkatoff = ffs_blkatoff; ump->um_truncate = ffs_truncate; ump->um_update = ffs_update; ump->um_valloc = ffs_valloc; ump->um_vfree = ffs_vfree; ump->um_ifree = ffs_ifree; ump->um_rdonly = ffs_rdonly; ump->um_snapgone = ffs_snapgone; if ((mp->mnt_flag & MNT_UNTRUSTED) != 0) ump->um_check_blkno = ffs_check_blkno; else ump->um_check_blkno = NULL; mtx_init(UFS_MTX(ump), "FFS", "FFS Lock", MTX_DEF); sx_init(&ump->um_checkpath_lock, "uchpth"); ffs_oldfscompat_read(fs, ump, fs->fs_sblockloc); fs->fs_ronly = ronly; fs->fs_active = NULL; mp->mnt_data = ump; mp->mnt_stat.f_fsid.val[0] = fs->fs_id[0]; mp->mnt_stat.f_fsid.val[1] = fs->fs_id[1]; nmp = NULL; if (fs->fs_id[0] == 0 || fs->fs_id[1] == 0 || (nmp = vfs_getvfs(&mp->mnt_stat.f_fsid))) { if (nmp) vfs_rel(nmp); vfs_getnewfsid(mp); } ump->um_bsize = fs->fs_bsize; ump->um_maxsymlinklen = fs->fs_maxsymlinklen; MNT_ILOCK(mp); mp->mnt_flag |= MNT_LOCAL; MNT_IUNLOCK(mp); if ((fs->fs_flags & FS_MULTILABEL) != 0) { #ifdef MAC MNT_ILOCK(mp); mp->mnt_flag |= MNT_MULTILABEL; MNT_IUNLOCK(mp); #else printf("WARNING: %s: multilabel flag on fs but " "no MAC support\n", mp->mnt_stat.f_mntonname); #endif } if ((fs->fs_flags & FS_ACLS) != 0) { #ifdef UFS_ACL MNT_ILOCK(mp); if (mp->mnt_flag & MNT_NFS4ACLS) printf("WARNING: %s: ACLs flag on fs conflicts with " "\"nfsv4acls\" mount option; option ignored\n", mp->mnt_stat.f_mntonname); mp->mnt_flag &= ~MNT_NFS4ACLS; mp->mnt_flag |= MNT_ACLS; MNT_IUNLOCK(mp); #else printf("WARNING: %s: ACLs flag on fs but no ACLs support\n", mp->mnt_stat.f_mntonname); #endif } if ((fs->fs_flags & FS_NFS4ACLS) != 0) { #ifdef UFS_ACL MNT_ILOCK(mp); if (mp->mnt_flag & MNT_ACLS) printf("WARNING: %s: NFSv4 ACLs flag on fs conflicts " "with \"acls\" mount option; option ignored\n", mp->mnt_stat.f_mntonname); mp->mnt_flag &= ~MNT_ACLS; mp->mnt_flag |= MNT_NFS4ACLS; MNT_IUNLOCK(mp); #else printf("WARNING: %s: NFSv4 ACLs flag on fs but no " "ACLs support\n", mp->mnt_stat.f_mntonname); #endif } if ((fs->fs_flags & FS_TRIM) != 0) { len = sizeof(int); if (g_io_getattr("GEOM::candelete", cp, &len, &candelete) == 0) { if (candelete) ump->um_flags |= UM_CANDELETE; else printf("WARNING: %s: TRIM flag on fs but disk " "does not support TRIM\n", mp->mnt_stat.f_mntonname); } else { printf("WARNING: %s: TRIM flag on fs but disk does " "not confirm that it supports TRIM\n", mp->mnt_stat.f_mntonname); } if (((ump->um_flags) & UM_CANDELETE) != 0) { ump->um_trim_tq = taskqueue_create("trim", M_WAITOK, taskqueue_thread_enqueue, &ump->um_trim_tq); taskqueue_start_threads(&ump->um_trim_tq, 1, PVFS, "%s trim", mp->mnt_stat.f_mntonname); ump->um_trimhash = hashinit(MAXTRIMIO, M_TRIM, &ump->um_trimlisthashsize); } } len = sizeof(int); if (g_io_getattr("GEOM::canspeedup", cp, &len, &canspeedup) == 0) { if (canspeedup) ump->um_flags |= UM_CANSPEEDUP; } ump->um_mountp = mp; ump->um_dev = dev; ump->um_devvp = devvp; ump->um_odevvp = odevvp; 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; #ifdef UFS_EXTATTR ufs_extattr_uepm_init(&ump->um_extattr); #endif /* * Set FS local "last mounted on" information (NULL pad) */ bzero(fs->fs_fsmnt, MAXMNTLEN); strlcpy(fs->fs_fsmnt, mp->mnt_stat.f_mntonname, MAXMNTLEN); mp->mnt_stat.f_iosize = fs->fs_bsize; 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; } if (ronly == 0) { fs->fs_mtime = time_second; if ((fs->fs_flags & FS_DOSOFTDEP) && (error = softdep_mount(devvp, mp, fs, cred)) != 0) { ffs_flushfiles(mp, FORCECLOSE, td); goto out; } if (fs->fs_snapinum[0] != 0) ffs_snapshot_mount(mp); fs->fs_fmod = 1; fs->fs_clean = 0; (void) ffs_sbupdate(ump, MNT_WAIT, 0); } /* * Initialize filesystem state information in mount struct. */ MNT_ILOCK(mp); mp->mnt_kern_flag |= MNTK_LOOKUP_SHARED | MNTK_EXTENDED_SHARED | MNTK_NO_IOPF | MNTK_UNMAPPED_BUFS | MNTK_USES_BCACHE; MNT_IUNLOCK(mp); #ifdef UFS_EXTATTR #ifdef UFS_EXTATTR_AUTOSTART /* * * Auto-starting does the following: * - check for /.attribute in the fs, and extattr_start if so * - for each file in .attribute, enable that file with * an attribute of the same name. * Not clear how to report errors -- probably eat them. * This would all happen while the filesystem was busy/not * available, so would effectively be "atomic". */ (void) ufs_extattr_autostart(mp, td); #endif /* !UFS_EXTATTR_AUTOSTART */ #endif /* !UFS_EXTATTR */ return (0); out: if (fs != NULL) { free(fs->fs_csp, M_UFSMNT); free(fs->fs_si, M_UFSMNT); free(fs, M_UFSMNT); } if (cp != NULL) { g_topology_lock(); g_vfs_close(cp); g_topology_unlock(); } if (ump != NULL) { mtx_destroy(UFS_MTX(ump)); sx_destroy(&ump->um_checkpath_lock); if (mp->mnt_gjprovider != NULL) { free(mp->mnt_gjprovider, M_UFSMNT); mp->mnt_gjprovider = NULL; } MPASS(ump->um_softdep == NULL); free(ump, M_UFSMNT); mp->mnt_data = NULL; } BO_LOCK(&odevvp->v_bufobj); odevvp->v_bufobj.bo_flag &= ~BO_NOBUFS; BO_UNLOCK(&odevvp->v_bufobj); atomic_store_rel_ptr((uintptr_t *)&dev->si_mountpt, 0); vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); mntfs_freevp(devvp); dev_rel(dev); return (error); } /* * A read function for use by filesystem-layer routines. */ static int ffs_use_bread(void *devfd, off_t loc, void **bufp, int size) { struct buf *bp; int error; KASSERT(*bufp == NULL, ("ffs_use_bread: non-NULL *bufp %p\n", *bufp)); *bufp = malloc(size, M_UFSMNT, M_WAITOK); if ((error = bread((struct vnode *)devfd, btodb(loc), size, NOCRED, &bp)) != 0) return (error); bcopy(bp->b_data, *bufp, size); bp->b_flags |= B_INVAL | B_NOCACHE; brelse(bp); return (0); } static int bigcgs = 0; SYSCTL_INT(_debug, OID_AUTO, bigcgs, CTLFLAG_RW, &bigcgs, 0, ""); /* * Sanity checks for loading old filesystem superblocks. * See ffs_oldfscompat_write below for unwound actions. * * XXX - Parts get retired eventually. * Unfortunately new bits get added. */ static void ffs_oldfscompat_read(struct fs *fs, struct ufsmount *ump, ufs2_daddr_t sblockloc) { off_t maxfilesize; /* * If not yet done, update fs_flags location and value of fs_sblockloc. */ if ((fs->fs_old_flags & FS_FLAGS_UPDATED) == 0) { fs->fs_flags = fs->fs_old_flags; fs->fs_old_flags |= FS_FLAGS_UPDATED; fs->fs_sblockloc = sblockloc; } /* * If not yet done, update UFS1 superblock with new wider fields. */ if (fs->fs_magic == FS_UFS1_MAGIC && fs->fs_maxbsize != fs->fs_bsize) { fs->fs_maxbsize = fs->fs_bsize; fs->fs_time = fs->fs_old_time; fs->fs_size = fs->fs_old_size; fs->fs_dsize = fs->fs_old_dsize; fs->fs_csaddr = fs->fs_old_csaddr; fs->fs_cstotal.cs_ndir = fs->fs_old_cstotal.cs_ndir; fs->fs_cstotal.cs_nbfree = fs->fs_old_cstotal.cs_nbfree; fs->fs_cstotal.cs_nifree = fs->fs_old_cstotal.cs_nifree; fs->fs_cstotal.cs_nffree = fs->fs_old_cstotal.cs_nffree; } if (fs->fs_magic == FS_UFS1_MAGIC && fs->fs_old_inodefmt < FS_44INODEFMT) { fs->fs_maxfilesize = ((uint64_t)1 << 31) - 1; fs->fs_qbmask = ~fs->fs_bmask; fs->fs_qfmask = ~fs->fs_fmask; } if (fs->fs_magic == FS_UFS1_MAGIC) { ump->um_savedmaxfilesize = fs->fs_maxfilesize; maxfilesize = (uint64_t)0x80000000 * fs->fs_bsize - 1; if (fs->fs_maxfilesize > maxfilesize) fs->fs_maxfilesize = maxfilesize; } /* Compatibility for old filesystems */ if (fs->fs_avgfilesize <= 0) fs->fs_avgfilesize = AVFILESIZ; if (fs->fs_avgfpdir <= 0) fs->fs_avgfpdir = AFPDIR; if (bigcgs) { fs->fs_save_cgsize = fs->fs_cgsize; fs->fs_cgsize = fs->fs_bsize; } } /* * Unwinding superblock updates for old filesystems. * See ffs_oldfscompat_read above for details. * * XXX - Parts get retired eventually. * Unfortunately new bits get added. */ void ffs_oldfscompat_write(struct fs *fs, struct ufsmount *ump) { /* * Copy back UFS2 updated fields that UFS1 inspects. */ if (fs->fs_magic == FS_UFS1_MAGIC) { fs->fs_old_time = fs->fs_time; fs->fs_old_cstotal.cs_ndir = fs->fs_cstotal.cs_ndir; fs->fs_old_cstotal.cs_nbfree = fs->fs_cstotal.cs_nbfree; fs->fs_old_cstotal.cs_nifree = fs->fs_cstotal.cs_nifree; fs->fs_old_cstotal.cs_nffree = fs->fs_cstotal.cs_nffree; fs->fs_maxfilesize = ump->um_savedmaxfilesize; } if (bigcgs) { fs->fs_cgsize = fs->fs_save_cgsize; fs->fs_save_cgsize = 0; } } /* * unmount system call */ static int ffs_unmount(struct mount *mp, int mntflags) { struct thread *td; struct ufsmount *ump = VFSTOUFS(mp); struct fs *fs; int error, flags, susp; #ifdef UFS_EXTATTR int e_restart; #endif flags = 0; td = curthread; fs = ump->um_fs; if (mntflags & MNT_FORCE) flags |= FORCECLOSE; susp = fs->fs_ronly == 0; #ifdef UFS_EXTATTR if ((error = ufs_extattr_stop(mp, td))) { if (error != EOPNOTSUPP) printf("WARNING: unmount %s: ufs_extattr_stop " "returned errno %d\n", mp->mnt_stat.f_mntonname, error); e_restart = 0; } else { ufs_extattr_uepm_destroy(&ump->um_extattr); e_restart = 1; } #endif if (susp) { error = vfs_write_suspend_umnt(mp); if (error != 0) goto fail1; } if (MOUNTEDSOFTDEP(mp)) error = softdep_flushfiles(mp, flags, td); else error = ffs_flushfiles(mp, flags, td); if (error != 0 && !ffs_fsfail_cleanup(ump, error)) goto fail; UFS_LOCK(ump); if (fs->fs_pendingblocks != 0 || fs->fs_pendinginodes != 0) { printf("WARNING: unmount %s: pending error: blocks %jd " "files %d\n", fs->fs_fsmnt, (intmax_t)fs->fs_pendingblocks, fs->fs_pendinginodes); fs->fs_pendingblocks = 0; fs->fs_pendinginodes = 0; } UFS_UNLOCK(ump); if (MOUNTEDSOFTDEP(mp)) softdep_unmount(mp); MPASS(ump->um_softdep == NULL); if (fs->fs_ronly == 0) { fs->fs_clean = fs->fs_flags & (FS_UNCLEAN|FS_NEEDSFSCK) ? 0 : 1; error = ffs_sbupdate(ump, MNT_WAIT, 0); if (ffs_fsfail_cleanup(ump, error)) error = 0; if (error != 0 && !ffs_fsfail_cleanup(ump, error)) { fs->fs_clean = 0; goto fail; } } if (susp) vfs_write_resume(mp, VR_START_WRITE); if (ump->um_trim_tq != NULL) { MPASS(ump->um_trim_inflight == 0); taskqueue_free(ump->um_trim_tq); free (ump->um_trimhash, M_TRIM); } vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY); g_topology_lock(); g_vfs_close(ump->um_cp); g_topology_unlock(); BO_LOCK(&ump->um_odevvp->v_bufobj); ump->um_odevvp->v_bufobj.bo_flag &= ~BO_NOBUFS; BO_UNLOCK(&ump->um_odevvp->v_bufobj); atomic_store_rel_ptr((uintptr_t *)&ump->um_dev->si_mountpt, 0); mntfs_freevp(ump->um_devvp); vrele(ump->um_odevvp); dev_rel(ump->um_dev); mtx_destroy(UFS_MTX(ump)); sx_destroy(&ump->um_checkpath_lock); if (mp->mnt_gjprovider != NULL) { free(mp->mnt_gjprovider, M_UFSMNT); mp->mnt_gjprovider = NULL; } free(fs->fs_csp, M_UFSMNT); free(fs->fs_si, M_UFSMNT); free(fs, M_UFSMNT); free(ump, M_UFSMNT); mp->mnt_data = NULL; MNT_ILOCK(mp); mp->mnt_flag &= ~MNT_LOCAL; MNT_IUNLOCK(mp); if (td->td_su == mp) { td->td_su = NULL; vfs_rel(mp); } return (error); fail: if (susp) vfs_write_resume(mp, VR_START_WRITE); fail1: #ifdef UFS_EXTATTR if (e_restart) { ufs_extattr_uepm_init(&ump->um_extattr); #ifdef UFS_EXTATTR_AUTOSTART (void) ufs_extattr_autostart(mp, td); #endif } #endif return (error); } /* * Flush out all the files in a filesystem. */ int ffs_flushfiles(struct mount *mp, int flags, struct thread *td) { struct ufsmount *ump; int qerror, error; ump = VFSTOUFS(mp); qerror = 0; #ifdef QUOTA if (mp->mnt_flag & MNT_QUOTA) { int i; error = vflush(mp, 0, SKIPSYSTEM|flags, td); if (error) return (error); for (i = 0; i < MAXQUOTAS; i++) { error = quotaoff(td, mp, i); if (error != 0) { if ((flags & EARLYFLUSH) == 0) return (error); else qerror = error; } } /* * Here we fall through to vflush again to ensure that * we have gotten rid of all the system vnodes, unless * quotas must not be closed. */ } #endif /* devvp is not locked there */ if (ump->um_devvp->v_vflag & VV_COPYONWRITE) { if ((error = vflush(mp, 0, SKIPSYSTEM | flags, td)) != 0) return (error); ffs_snapshot_unmount(mp); flags |= FORCECLOSE; /* * Here we fall through to vflush again to ensure * that we have gotten rid of all the system vnodes. */ } /* * Do not close system files if quotas were not closed, to be * able to sync the remaining dquots. The freeblks softupdate * workitems might hold a reference on a dquot, preventing * quotaoff() from completing. Next round of * softdep_flushworklist() iteration should process the * blockers, allowing the next run of quotaoff() to finally * flush held dquots. * * Otherwise, flush all the files. */ if (qerror == 0 && (error = vflush(mp, 0, flags, td)) != 0) return (error); /* * If this is a forcible unmount and there were any files that * were unlinked but still open, then vflush() will have * truncated and freed those files, which might have started * some trim work. Wait here for any trims to complete * and process the blkfrees which follow the trims. * This may create more dirty devvp buffers and softdep deps. */ if (ump->um_trim_tq != NULL) { while (ump->um_trim_inflight != 0) pause("ufsutr", hz); taskqueue_drain_all(ump->um_trim_tq); } /* * Flush filesystem metadata. */ vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY); error = VOP_FSYNC(ump->um_devvp, MNT_WAIT, td); VOP_UNLOCK(ump->um_devvp); return (error); } /* * Get filesystem statistics. */ static int ffs_statfs(struct mount *mp, struct statfs *sbp) { struct ufsmount *ump; struct fs *fs; ump = VFSTOUFS(mp); fs = ump->um_fs; if (fs->fs_magic != FS_UFS1_MAGIC && fs->fs_magic != FS_UFS2_MAGIC) panic("ffs_statfs"); sbp->f_version = STATFS_VERSION; sbp->f_bsize = fs->fs_fsize; sbp->f_iosize = fs->fs_bsize; sbp->f_blocks = fs->fs_dsize; UFS_LOCK(ump); sbp->f_bfree = fs->fs_cstotal.cs_nbfree * fs->fs_frag + fs->fs_cstotal.cs_nffree + dbtofsb(fs, fs->fs_pendingblocks); sbp->f_bavail = freespace(fs, fs->fs_minfree) + dbtofsb(fs, fs->fs_pendingblocks); sbp->f_files = fs->fs_ncg * fs->fs_ipg - UFS_ROOTINO; sbp->f_ffree = fs->fs_cstotal.cs_nifree + fs->fs_pendinginodes; UFS_UNLOCK(ump); sbp->f_namemax = UFS_MAXNAMLEN; return (0); } static bool sync_doupdate(struct inode *ip) { return ((ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) != 0); } static int ffs_sync_lazy_filter(struct vnode *vp, void *arg __unused) { struct inode *ip; /* * Flags are safe to access because ->v_data invalidation * is held off by listmtx. */ if (vp->v_type == VNON) return (false); ip = VTOI(vp); if (!sync_doupdate(ip) && (vp->v_iflag & VI_OWEINACT) == 0) return (false); return (true); } /* * For a lazy sync, we only care about access times, quotas and the * superblock. Other filesystem changes are already converted to * cylinder group blocks or inode blocks updates and are written to * disk by syncer. */ static int ffs_sync_lazy(struct mount *mp) { struct vnode *mvp, *vp; struct inode *ip; int allerror, error; allerror = 0; if ((mp->mnt_flag & MNT_NOATIME) != 0) { #ifdef QUOTA qsync(mp); #endif goto sbupdate; } MNT_VNODE_FOREACH_LAZY(vp, mp, mvp, ffs_sync_lazy_filter, NULL) { if (vp->v_type == VNON) { VI_UNLOCK(vp); continue; } ip = VTOI(vp); /* * The IN_ACCESS flag is converted to IN_MODIFIED by * ufs_close() and ufs_getattr() by the calls to * ufs_itimes_locked(), without subsequent UFS_UPDATE(). * Test also all the other timestamp flags too, to pick up * any other cases that could be missed. */ if (!sync_doupdate(ip) && (vp->v_iflag & VI_OWEINACT) == 0) { VI_UNLOCK(vp); continue; } if ((error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK)) != 0) continue; #ifdef QUOTA qsyncvp(vp); #endif if (sync_doupdate(ip)) error = ffs_update(vp, 0); if (error != 0) allerror = error; vput(vp); } sbupdate: if (VFSTOUFS(mp)->um_fs->fs_fmod != 0 && (error = ffs_sbupdate(VFSTOUFS(mp), MNT_LAZY, 0)) != 0) allerror = error; return (allerror); } /* * 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 busy using * vfs_busy(). */ static int ffs_sync(struct mount *mp, int waitfor) { struct vnode *mvp, *vp, *devvp; struct thread *td; struct inode *ip; struct ufsmount *ump = VFSTOUFS(mp); struct fs *fs; int error, count, lockreq, allerror = 0; int suspend; int suspended; int secondary_writes; int secondary_accwrites; int softdep_deps; int softdep_accdeps; struct bufobj *bo; suspend = 0; suspended = 0; td = curthread; fs = ump->um_fs; if (fs->fs_fmod != 0 && fs->fs_ronly != 0) panic("%s: ffs_sync: modification on read-only filesystem", fs->fs_fsmnt); if (waitfor == MNT_LAZY) { if (!rebooting) return (ffs_sync_lazy(mp)); waitfor = MNT_NOWAIT; } /* * Write back each (modified) inode. */ lockreq = LK_EXCLUSIVE | LK_NOWAIT; if (waitfor == MNT_SUSPEND) { suspend = 1; waitfor = MNT_WAIT; } if (waitfor == MNT_WAIT) lockreq = LK_EXCLUSIVE; lockreq |= LK_INTERLOCK | LK_SLEEPFAIL; loop: /* Grab snapshot of secondary write counts */ MNT_ILOCK(mp); secondary_writes = mp->mnt_secondary_writes; secondary_accwrites = mp->mnt_secondary_accwrites; MNT_IUNLOCK(mp); /* Grab snapshot of softdep dependency counts */ softdep_get_depcounts(mp, &softdep_deps, &softdep_accdeps); MNT_VNODE_FOREACH_ALL(vp, mp, mvp) { /* * Depend on the vnode interlock to keep things stable enough * for a quick test. Since there might be hundreds of * thousands of vnodes, we cannot afford even a subroutine * call unless there's a good chance that we have work to do. */ if (vp->v_type == VNON) { VI_UNLOCK(vp); continue; } ip = VTOI(vp); if ((ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0 && vp->v_bufobj.bo_dirty.bv_cnt == 0) { VI_UNLOCK(vp); continue; } if ((error = vget(vp, lockreq)) != 0) { if (error == ENOENT || error == ENOLCK) { MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp); goto loop; } continue; } #ifdef QUOTA qsyncvp(vp); #endif for (;;) { error = ffs_syncvnode(vp, waitfor, 0); if (error == ERELOOKUP) continue; if (error != 0) allerror = error; break; } vput(vp); } /* * Force stale filesystem control information to be flushed. */ if (waitfor == MNT_WAIT || rebooting) { if ((error = softdep_flushworklist(ump->um_mountp, &count, td))) allerror = error; if (ffs_fsfail_cleanup(ump, allerror)) allerror = 0; /* Flushed work items may create new vnodes to clean */ if (allerror == 0 && count) goto loop; } devvp = ump->um_devvp; bo = &devvp->v_bufobj; BO_LOCK(bo); if (bo->bo_numoutput > 0 || bo->bo_dirty.bv_cnt > 0) { BO_UNLOCK(bo); vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); error = VOP_FSYNC(devvp, waitfor, td); VOP_UNLOCK(devvp); if (MOUNTEDSOFTDEP(mp) && (error == 0 || error == EAGAIN)) error = ffs_sbupdate(ump, waitfor, 0); if (error != 0) allerror = error; if (ffs_fsfail_cleanup(ump, allerror)) allerror = 0; if (allerror == 0 && waitfor == MNT_WAIT) goto loop; } else if (suspend != 0) { if (softdep_check_suspend(mp, devvp, softdep_deps, softdep_accdeps, secondary_writes, secondary_accwrites) != 0) { MNT_IUNLOCK(mp); goto loop; /* More work needed */ } mtx_assert(MNT_MTX(mp), MA_OWNED); mp->mnt_kern_flag |= MNTK_SUSPEND2 | MNTK_SUSPENDED; MNT_IUNLOCK(mp); suspended = 1; } else BO_UNLOCK(bo); /* * Write back modified superblock. */ if (fs->fs_fmod != 0 && (error = ffs_sbupdate(ump, waitfor, suspended)) != 0) allerror = error; if (ffs_fsfail_cleanup(ump, allerror)) allerror = 0; return (allerror); } int ffs_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp) { return (ffs_vgetf(mp, ino, flags, vpp, 0)); } int ffs_vgetf(struct mount *mp, ino_t ino, int flags, struct vnode **vpp, int ffs_flags) { struct fs *fs; struct inode *ip; struct ufsmount *ump; struct buf *bp; struct vnode *vp; daddr_t dbn; int error; MPASS((ffs_flags & (FFSV_REPLACE | FFSV_REPLACE_DOOMED)) == 0 || (flags & LK_EXCLUSIVE) != 0); error = vfs_hash_get(mp, ino, flags, curthread, vpp, NULL, NULL); if (error != 0) return (error); if (*vpp != NULL) { if ((ffs_flags & FFSV_REPLACE) == 0 || ((ffs_flags & FFSV_REPLACE_DOOMED) == 0 || !VN_IS_DOOMED(*vpp))) return (0); vgone(*vpp); vput(*vpp); } /* * We must promote to an exclusive lock for vnode creation. This * can happen if lookup is passed LOCKSHARED. */ if ((flags & LK_TYPE_MASK) == LK_SHARED) { flags &= ~LK_TYPE_MASK; flags |= LK_EXCLUSIVE; } /* * We do not lock vnode creation as it is believed to be too * expensive for such rare case as simultaneous creation of vnode * for same ino by different processes. We just allow them to race * and check later to decide who wins. Let the race begin! */ ump = VFSTOUFS(mp); fs = ump->um_fs; ip = uma_zalloc_smr(uma_inode, M_WAITOK | M_ZERO); /* Allocate a new vnode/inode. */ error = getnewvnode("ufs", mp, fs->fs_magic == FS_UFS1_MAGIC ? &ffs_vnodeops1 : &ffs_vnodeops2, &vp); if (error) { *vpp = NULL; uma_zfree_smr(uma_inode, ip); return (error); } /* * FFS supports recursive locking. */ lockmgr(vp->v_vnlock, LK_EXCLUSIVE | LK_NOWITNESS, NULL); VN_LOCK_AREC(vp); vp->v_data = ip; vp->v_bufobj.bo_bsize = fs->fs_bsize; ip->i_vnode = vp; ip->i_ump = ump; ip->i_number = ino; ip->i_ea_refs = 0; ip->i_nextclustercg = -1; ip->i_flag = fs->fs_magic == FS_UFS1_MAGIC ? 0 : IN_UFS2; ip->i_mode = 0; /* ensure error cases below throw away vnode */ cluster_init_vn(&ip->i_clusterw); #ifdef DIAGNOSTIC ufs_init_trackers(ip); #endif #ifdef QUOTA { int i; for (i = 0; i < MAXQUOTAS; i++) ip->i_dquot[i] = NODQUOT; } #endif if (ffs_flags & FFSV_FORCEINSMQ) vp->v_vflag |= VV_FORCEINSMQ; error = insmntque(vp, mp); if (error != 0) { uma_zfree_smr(uma_inode, ip); *vpp = NULL; return (error); } vp->v_vflag &= ~VV_FORCEINSMQ; error = vfs_hash_insert(vp, ino, flags, curthread, vpp, NULL, NULL); if (error != 0) return (error); if (*vpp != NULL) { /* * Calls from ffs_valloc() (i.e. FFSV_REPLACE set) * operate on empty inode, which must not be found by * other threads until fully filled. Vnode for empty * inode must be not re-inserted on the hash by other * thread, after removal by us at the beginning. */ MPASS((ffs_flags & FFSV_REPLACE) == 0); return (0); } /* Read in the disk contents for the inode, copy into the inode. */ dbn = fsbtodb(fs, ino_to_fsba(fs, ino)); error = ffs_breadz(ump, ump->um_devvp, dbn, dbn, (int)fs->fs_bsize, NULL, NULL, 0, NOCRED, 0, NULL, &bp); if (error != 0) { /* * 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(). */ vgone(vp); vput(vp); *vpp = NULL; return (error); } if (I_IS_UFS1(ip)) ip->i_din1 = uma_zalloc(uma_ufs1, M_WAITOK); else ip->i_din2 = uma_zalloc(uma_ufs2, M_WAITOK); if ((error = ffs_load_inode(bp, ip, fs, ino)) != 0) { bqrelse(bp); vgone(vp); vput(vp); *vpp = NULL; return (error); } if (DOINGSOFTDEP(vp) && (!fs->fs_ronly || (ffs_flags & FFSV_FORCEINODEDEP) != 0)) softdep_load_inodeblock(ip); else ip->i_effnlink = ip->i_nlink; bqrelse(bp); /* * Initialize the vnode from the inode, check for aliases. * Note that the underlying vnode may have changed. */ error = ufs_vinit(mp, I_IS_UFS1(ip) ? &ffs_fifoops1 : &ffs_fifoops2, &vp); if (error) { vgone(vp); vput(vp); *vpp = NULL; return (error); } /* * Finish inode initialization. */ if (vp->v_type != VFIFO) { /* FFS supports shared locking for all files except fifos. */ VN_LOCK_ASHARE(vp); } /* * 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_gen == 0) { while (ip->i_gen == 0) ip->i_gen = arc4random(); if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { UFS_INODE_SET_FLAG(ip, IN_MODIFIED); DIP_SET(ip, i_gen, ip->i_gen); } } #ifdef MAC if ((mp->mnt_flag & MNT_MULTILABEL) && ip->i_mode) { /* * If this vnode is already allocated, and we're running * multi-label, attempt to perform a label association * from the extended attributes on the inode. */ error = mac_vnode_associate_extattr(mp, vp); if (error) { /* ufs_inactive will release ip->i_devvp ref. */ vgone(vp); vput(vp); *vpp = NULL; return (error); } } #endif *vpp = vp; return (0); } /* * File handle to vnode * * Have to be really careful about stale file handles: * - check that the inode number is valid * - for UFS2 check that the inode number is initialized * - call ffs_vget() to get the locked inode * - check for an unallocated inode (i_mode == 0) * - check that the given client host has export rights and return * those rights via. exflagsp and credanonp */ static int ffs_fhtovp(struct mount *mp, struct fid *fhp, int flags, struct vnode **vpp) { struct ufid *ufhp; ufhp = (struct ufid *)fhp; return (ffs_inotovp(mp, ufhp->ufid_ino, ufhp->ufid_gen, flags, vpp, 0)); } int ffs_inotovp(struct mount *mp, ino_t ino, u_int64_t gen, int lflags, struct vnode **vpp, int ffs_flags) { struct ufsmount *ump; struct vnode *nvp; struct inode *ip; struct fs *fs; struct cg *cgp; struct buf *bp; u_int cg; int error; ump = VFSTOUFS(mp); fs = ump->um_fs; *vpp = NULL; if (ino < UFS_ROOTINO || ino >= fs->fs_ncg * fs->fs_ipg) return (ESTALE); /* * Need to check if inode is initialized because UFS2 does lazy * initialization and nfs_fhtovp can offer arbitrary inode numbers. */ if (fs->fs_magic == FS_UFS2_MAGIC) { cg = ino_to_cg(fs, ino); error = ffs_getcg(fs, ump->um_devvp, cg, 0, &bp, &cgp); if (error != 0) return (error); if (ino >= cg * fs->fs_ipg + cgp->cg_initediblk) { brelse(bp); return (ESTALE); } brelse(bp); } error = ffs_vgetf(mp, ino, lflags, &nvp, ffs_flags); if (error != 0) return (error); ip = VTOI(nvp); if (ip->i_mode == 0 || ip->i_gen != gen || ip->i_effnlink <= 0) { if (ip->i_mode == 0) vgone(nvp); vput(nvp); return (ESTALE); } vnode_create_vobject(nvp, DIP(ip, i_size), curthread); *vpp = nvp; return (0); } /* * Initialize the filesystem. */ static int ffs_init(struct vfsconf *vfsp) { ffs_susp_initialize(); softdep_initialize(); return (ufs_init(vfsp)); } /* * Undo the work of ffs_init(). */ static int ffs_uninit(struct vfsconf *vfsp) { int ret; ret = ufs_uninit(vfsp); softdep_uninitialize(); ffs_susp_uninitialize(); taskqueue_drain_all(taskqueue_thread); return (ret); } /* * Structure used to pass information from ffs_sbupdate to its * helper routine ffs_use_bwrite. */ struct devfd { struct ufsmount *ump; struct buf *sbbp; int waitfor; int suspended; int error; }; /* * Write a superblock and associated information back to disk. */ int ffs_sbupdate(struct ufsmount *ump, int waitfor, int suspended) { struct fs *fs; struct buf *sbbp; struct devfd devfd; fs = ump->um_fs; if (fs->fs_ronly == 1 && (ump->um_mountp->mnt_flag & (MNT_RDONLY | MNT_UPDATE)) != (MNT_RDONLY | MNT_UPDATE)) panic("ffs_sbupdate: write read-only filesystem"); /* * We use the superblock's buf to serialize calls to ffs_sbupdate(). */ sbbp = getblk(ump->um_devvp, btodb(fs->fs_sblockloc), (int)fs->fs_sbsize, 0, 0, 0); /* * Initialize info needed for write function. */ devfd.ump = ump; devfd.sbbp = sbbp; devfd.waitfor = waitfor; devfd.suspended = suspended; devfd.error = 0; return (ffs_sbput(&devfd, fs, fs->fs_sblockloc, ffs_use_bwrite)); } /* * Write function for use by filesystem-layer routines. */ static int ffs_use_bwrite(void *devfd, off_t loc, void *buf, int size) { struct devfd *devfdp; struct ufsmount *ump; struct buf *bp; struct fs *fs; int error; devfdp = devfd; ump = devfdp->ump; fs = ump->um_fs; /* * Writing the superblock summary information. */ if (loc != fs->fs_sblockloc) { bp = getblk(ump->um_devvp, btodb(loc), size, 0, 0, 0); bcopy(buf, bp->b_data, (u_int)size); if (devfdp->suspended) bp->b_flags |= B_VALIDSUSPWRT; if (devfdp->waitfor != MNT_WAIT) bawrite(bp); else if ((error = bwrite(bp)) != 0) devfdp->error = error; return (0); } /* * Writing the superblock itself. We need to do special checks for it. */ bp = devfdp->sbbp; if (ffs_fsfail_cleanup(ump, devfdp->error)) devfdp->error = 0; if (devfdp->error != 0) { brelse(bp); return (devfdp->error); } if (fs->fs_magic == FS_UFS1_MAGIC && fs->fs_sblockloc != SBLOCK_UFS1 && (fs->fs_old_flags & FS_FLAGS_UPDATED) == 0) { printf("WARNING: %s: correcting fs_sblockloc from %jd to %d\n", fs->fs_fsmnt, fs->fs_sblockloc, SBLOCK_UFS1); fs->fs_sblockloc = SBLOCK_UFS1; } if (fs->fs_magic == FS_UFS2_MAGIC && fs->fs_sblockloc != SBLOCK_UFS2 && (fs->fs_old_flags & FS_FLAGS_UPDATED) == 0) { printf("WARNING: %s: correcting fs_sblockloc from %jd to %d\n", fs->fs_fsmnt, fs->fs_sblockloc, SBLOCK_UFS2); fs->fs_sblockloc = SBLOCK_UFS2; } if (MOUNTEDSOFTDEP(ump->um_mountp)) softdep_setup_sbupdate(ump, (struct fs *)bp->b_data, bp); UFS_LOCK(ump); bcopy((caddr_t)fs, bp->b_data, (u_int)fs->fs_sbsize); UFS_UNLOCK(ump); fs = (struct fs *)bp->b_data; fs->fs_fmod = 0; ffs_oldfscompat_write(fs, ump); fs->fs_si = NULL; /* Recalculate the superblock hash */ fs->fs_ckhash = ffs_calc_sbhash(fs); if (devfdp->suspended) bp->b_flags |= B_VALIDSUSPWRT; if (devfdp->waitfor != MNT_WAIT) bawrite(bp); else if ((error = bwrite(bp)) != 0) devfdp->error = error; return (devfdp->error); } static int ffs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp, int attrnamespace, const char *attrname) { #ifdef UFS_EXTATTR return (ufs_extattrctl(mp, cmd, filename_vp, attrnamespace, attrname)); #else return (vfs_stdextattrctl(mp, cmd, filename_vp, attrnamespace, attrname)); #endif } static void ffs_ifree(struct ufsmount *ump, struct inode *ip) { if (ump->um_fstype == UFS1 && ip->i_din1 != NULL) uma_zfree(uma_ufs1, ip->i_din1); else if (ip->i_din2 != NULL) uma_zfree(uma_ufs2, ip->i_din2); uma_zfree_smr(uma_inode, ip); } static int dobkgrdwrite = 1; SYSCTL_INT(_debug, OID_AUTO, dobkgrdwrite, CTLFLAG_RW, &dobkgrdwrite, 0, "Do background writes (honoring the BV_BKGRDWRITE flag)?"); /* * Complete a background write started from bwrite. */ static void ffs_backgroundwritedone(struct buf *bp) { struct bufobj *bufobj; struct buf *origbp; #ifdef SOFTUPDATES if (!LIST_EMPTY(&bp->b_dep) && (bp->b_ioflags & BIO_ERROR) != 0) softdep_handle_error(bp); #endif /* * Find the original buffer that we are writing. */ bufobj = bp->b_bufobj; BO_LOCK(bufobj); if ((origbp = gbincore(bp->b_bufobj, bp->b_lblkno)) == NULL) panic("backgroundwritedone: lost buffer"); /* * We should mark the cylinder group buffer origbp as * dirty, to not lose the failed write. */ if ((bp->b_ioflags & BIO_ERROR) != 0) origbp->b_vflags |= BV_BKGRDERR; BO_UNLOCK(bufobj); /* * Process dependencies then return any unfinished ones. */ if (!LIST_EMPTY(&bp->b_dep) && (bp->b_ioflags & BIO_ERROR) == 0) buf_complete(bp); #ifdef SOFTUPDATES if (!LIST_EMPTY(&bp->b_dep)) softdep_move_dependencies(bp, origbp); #endif /* * This buffer is marked B_NOCACHE so when it is released * by biodone it will be tossed. Clear B_IOSTARTED in case of error. */ bp->b_flags |= B_NOCACHE; bp->b_flags &= ~(B_CACHE | B_IOSTARTED); pbrelvp(bp); /* * Prevent brelse() from trying to keep and re-dirtying bp on * errors. It causes b_bufobj dereference in * bdirty()/reassignbuf(), and b_bufobj was cleared in * pbrelvp() above. */ if ((bp->b_ioflags & BIO_ERROR) != 0) bp->b_flags |= B_INVAL; bufdone(bp); BO_LOCK(bufobj); /* * Clear the BV_BKGRDINPROG flag in the original buffer * and awaken it if it is waiting for the write to complete. * If BV_BKGRDINPROG is not set in the original buffer it must * have been released and re-instantiated - which is not legal. */ KASSERT((origbp->b_vflags & BV_BKGRDINPROG), ("backgroundwritedone: lost buffer2")); origbp->b_vflags &= ~BV_BKGRDINPROG; if (origbp->b_vflags & BV_BKGRDWAIT) { origbp->b_vflags &= ~BV_BKGRDWAIT; wakeup(&origbp->b_xflags); } BO_UNLOCK(bufobj); } /* * Write, release buffer on completion. (Done by iodone * if async). Do not bother writing anything if the buffer * is invalid. * * Note that we set B_CACHE here, indicating that buffer is * fully valid and thus cacheable. This is true even of NFS * now so we set it generally. This could be set either here * or in biodone() since the I/O is synchronous. We put it * here. */ static int ffs_bufwrite(struct buf *bp) { struct buf *newbp; struct cg *cgp; CTR3(KTR_BUF, "bufwrite(%p) vp %p flags %X", bp, bp->b_vp, bp->b_flags); if (bp->b_flags & B_INVAL) { brelse(bp); return (0); } if (!BUF_ISLOCKED(bp)) panic("bufwrite: buffer is not busy???"); /* * If a background write is already in progress, delay * writing this block if it is asynchronous. Otherwise * wait for the background write to complete. */ BO_LOCK(bp->b_bufobj); if (bp->b_vflags & BV_BKGRDINPROG) { if (bp->b_flags & B_ASYNC) { BO_UNLOCK(bp->b_bufobj); bdwrite(bp); return (0); } bp->b_vflags |= BV_BKGRDWAIT; msleep(&bp->b_xflags, BO_LOCKPTR(bp->b_bufobj), PRIBIO, "bwrbg", 0); if (bp->b_vflags & BV_BKGRDINPROG) panic("bufwrite: still writing"); } bp->b_vflags &= ~BV_BKGRDERR; BO_UNLOCK(bp->b_bufobj); /* * If this buffer is marked for background writing and we * do not have to wait for it, make a copy and write the * copy so as to leave this buffer ready for further use. * * This optimization eats a lot of memory. If we have a page * or buffer shortfall we can't do it. */ if (dobkgrdwrite && (bp->b_xflags & BX_BKGRDWRITE) && (bp->b_flags & B_ASYNC) && !vm_page_count_severe() && !buf_dirty_count_severe()) { KASSERT(bp->b_iodone == NULL, ("bufwrite: needs chained iodone (%p)", bp->b_iodone)); /* get a new block */ newbp = geteblk(bp->b_bufsize, GB_NOWAIT_BD); if (newbp == NULL) goto normal_write; KASSERT(buf_mapped(bp), ("Unmapped cg")); memcpy(newbp->b_data, bp->b_data, bp->b_bufsize); BO_LOCK(bp->b_bufobj); bp->b_vflags |= BV_BKGRDINPROG; BO_UNLOCK(bp->b_bufobj); newbp->b_xflags |= (bp->b_xflags & BX_FSPRIV) | BX_BKGRDMARKER; newbp->b_lblkno = bp->b_lblkno; newbp->b_blkno = bp->b_blkno; newbp->b_offset = bp->b_offset; newbp->b_iodone = ffs_backgroundwritedone; newbp->b_flags |= B_ASYNC; newbp->b_flags &= ~B_INVAL; pbgetvp(bp->b_vp, newbp); #ifdef SOFTUPDATES /* * Move over the dependencies. If there are rollbacks, * leave the parent buffer dirtied as it will need to * be written again. */ if (LIST_EMPTY(&bp->b_dep) || softdep_move_dependencies(bp, newbp) == 0) bundirty(bp); #else bundirty(bp); #endif /* * Initiate write on the copy, release the original. The * BKGRDINPROG flag prevents it from going away until * the background write completes. We have to recalculate * its check hash in case the buffer gets freed and then * reconstituted from the buffer cache during a later read. */ if ((bp->b_xflags & BX_CYLGRP) != 0) { cgp = (struct cg *)bp->b_data; cgp->cg_ckhash = 0; cgp->cg_ckhash = calculate_crc32c(~0L, bp->b_data, bp->b_bcount); } bqrelse(bp); bp = newbp; } else /* Mark the buffer clean */ bundirty(bp); /* Let the normal bufwrite do the rest for us */ normal_write: /* * If we are writing a cylinder group, update its time. */ if ((bp->b_xflags & BX_CYLGRP) != 0) { cgp = (struct cg *)bp->b_data; cgp->cg_old_time = cgp->cg_time = time_second; } return (bufwrite(bp)); } static void ffs_geom_strategy(struct bufobj *bo, struct buf *bp) { struct vnode *vp; struct buf *tbp; int error, nocopy; /* * This is the bufobj strategy for the private VCHR vnodes * used by FFS to access the underlying storage device. * We override the default bufobj strategy and thus bypass * VOP_STRATEGY() for these vnodes. */ vp = bo2vnode(bo); KASSERT(bp->b_vp == NULL || bp->b_vp->v_type != VCHR || bp->b_vp->v_rdev == NULL || bp->b_vp->v_rdev->si_mountpt == NULL || VFSTOUFS(bp->b_vp->v_rdev->si_mountpt) == NULL || vp == VFSTOUFS(bp->b_vp->v_rdev->si_mountpt)->um_devvp, ("ffs_geom_strategy() with wrong vp")); if (bp->b_iocmd == BIO_WRITE) { if ((bp->b_flags & B_VALIDSUSPWRT) == 0 && bp->b_vp != NULL && bp->b_vp->v_mount != NULL && (bp->b_vp->v_mount->mnt_kern_flag & MNTK_SUSPENDED) != 0) panic("ffs_geom_strategy: bad I/O"); nocopy = bp->b_flags & B_NOCOPY; bp->b_flags &= ~(B_VALIDSUSPWRT | B_NOCOPY); if ((vp->v_vflag & VV_COPYONWRITE) && nocopy == 0 && vp->v_rdev->si_snapdata != NULL) { if ((bp->b_flags & B_CLUSTER) != 0) { runningbufwakeup(bp); TAILQ_FOREACH(tbp, &bp->b_cluster.cluster_head, b_cluster.cluster_entry) { error = ffs_copyonwrite(vp, tbp); if (error != 0 && error != EOPNOTSUPP) { bp->b_error = error; bp->b_ioflags |= BIO_ERROR; bp->b_flags &= ~B_BARRIER; bufdone(bp); return; } } bp->b_runningbufspace = bp->b_bufsize; atomic_add_long(&runningbufspace, bp->b_runningbufspace); } else { error = ffs_copyonwrite(vp, bp); if (error != 0 && error != EOPNOTSUPP) { bp->b_error = error; bp->b_ioflags |= BIO_ERROR; bp->b_flags &= ~B_BARRIER; bufdone(bp); return; } } } #ifdef SOFTUPDATES if ((bp->b_flags & B_CLUSTER) != 0) { TAILQ_FOREACH(tbp, &bp->b_cluster.cluster_head, b_cluster.cluster_entry) { if (!LIST_EMPTY(&tbp->b_dep)) buf_start(tbp); } } else { if (!LIST_EMPTY(&bp->b_dep)) buf_start(bp); } #endif /* * Check for metadata that needs check-hashes and update them. */ switch (bp->b_xflags & BX_FSPRIV) { case BX_CYLGRP: ((struct cg *)bp->b_data)->cg_ckhash = 0; ((struct cg *)bp->b_data)->cg_ckhash = calculate_crc32c(~0L, bp->b_data, bp->b_bcount); break; case BX_SUPERBLOCK: case BX_INODE: case BX_INDIR: case BX_DIR: printf("Check-hash write is unimplemented!!!\n"); break; case 0: break; default: printf("multiple buffer types 0x%b\n", (u_int)(bp->b_xflags & BX_FSPRIV), PRINT_UFS_BUF_XFLAGS); break; } } if (bp->b_iocmd != BIO_READ && ffs_enxio_enable) bp->b_xflags |= BX_CVTENXIO; g_vfs_strategy(bo, bp); } int ffs_own_mount(const struct mount *mp) { if (mp->mnt_op == &ufs_vfsops) return (1); return (0); } #ifdef DDB #ifdef SOFTUPDATES /* defined in ffs_softdep.c */ extern void db_print_ffs(struct ufsmount *ump); DB_SHOW_COMMAND(ffs, db_show_ffs) { struct mount *mp; struct ufsmount *ump; if (have_addr) { ump = VFSTOUFS((struct mount *)addr); db_print_ffs(ump); return; } TAILQ_FOREACH(mp, &mountlist, mnt_list) { if (!strcmp(mp->mnt_stat.f_fstypename, ufs_vfsconf.vfc_name)) db_print_ffs(VFSTOUFS(mp)); } } #endif /* SOFTUPDATES */ #endif /* DDB */ diff --git a/sys/ufs/ffs/fs.h b/sys/ufs/ffs/fs.h index 8c0047f17caf..81cb416b383d 100644 --- a/sys/ufs/ffs/fs.h +++ b/sys/ufs/ffs/fs.h @@ -1,889 +1,893 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * 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 * $FreeBSD$ */ #ifndef _UFS_FFS_FS_H_ #define _UFS_FFS_FS_H_ #include #include /* * 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. */ #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 } /* * Request standard superblock location in ffs_sbget(). + */ +#define UFS_STDSB -1 /* Search standard places for superblock */ + +/* + * UFS_NOMSG indicates that superblock inconsistency error messages + * should not be printed. It is used by programs like fsck that + * want to print their own error message. * - * STDSB will fail if the superblock has a check hash and it is wrong. - * - * STDSB_NOHASHFAIL will note that the check hash is wrong but will - * still return the superblock. This is used by the bootstrap code - * to give the system a chance to come up so that fsck can be run - * to correct the problem. - * - * STDSB_NOMSG is the same as STDSB but the kernel does not print an - * error message. It is used by programs like fsck that want to - * print their own error message. + * UFS_NOCSUM causes only the superblock itself to be returned, but does + * not read in any auxiliary data structures like the cylinder group + * summary information. It is used by clients like glabel that just + * want to check for possible filesystem types. Using UFS_NOCSUM + * skips the superblock checks for csum data which allows superblocks + * that have corrupted csum data to be read and used. * - * STDSB_NOHASHFAIL_NOMSG is the same as STDSB_NOHASHFAIL but the kernel - * does not print an error message. It is used by clients like glabel - * that just want to check for possible filesystem types. - */ -#define STDSB -1 /* Fail if check-hash is bad */ -#define STDSB_NOHASHFAIL -2 /* Ignore check-hash failure */ -#define STDSB_NOMSG -3 /* STDSB with no kernel message */ -#define STDSB_NOHASHFAIL_NOMSG -4 /* STDSB_NOHASHFAIL with no message */ + * UFS_NOHASHFAIL will note that the check hash is wrong but will still + * return the superblock. This is used by the bootstrap code to + * give the system a chance to come up so that fsck can be run to + * correct the problem. + */ +#define UFS_NOHASHFAIL 0x0001 /* Ignore check-hash failure */ +#define UFS_NOMSG 0x0004 /* Print no error message */ +#define UFS_NOCSUM 0x0008 /* Read just the superblock without csum */ +#define UFS_ALTSBLK 0x1000 /* Flag used internally */ /* * 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'. File system 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 SBLOCKSIZE, * and that both SBLOCKSIZE 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. */ #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 a few * pointers and the remaining space is padded with fs_ocsp[]. * * NOCSPTRS determines the size of this padding. Historically this * space was used to store pointers to structures that summaried * filesystem usage and layout information. However, these pointers * left various kernel pointers in the superblock which made otherwise * identical superblocks appear to have differences. So, all the * pointers in the superblock were moved to a fs_summary_info structure * reducing the superblock to having only a single pointer to this * structure. When writing the superblock to disk, this pointer is * temporarily NULL'ed out so that the kernel pointer will not appear * in the on-disk copy of the superblock. */ #define NOCSPTRS ((128 / sizeof(void *)) - 1) /* * 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 8 #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 */ /* * 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 ((ufs2_daddr_t)(1)) #define BLK_SNAP ((ufs2_daddr_t)(2)) /* * Sysctl values for the fast filesystem. */ #define FFS_ADJ_REFCNT 1 /* adjust inode reference count */ #define FFS_ADJ_BLKCNT 2 /* adjust inode used block count */ #define FFS_BLK_FREE 3 /* free range of blocks in map */ #define FFS_DIR_FREE 4 /* free specified dir inodes in map */ #define FFS_FILE_FREE 5 /* free specified file inodes in map */ #define FFS_SET_FLAGS 6 /* set filesystem flags */ #define FFS_ADJ_NDIR 7 /* adjust number of directories */ #define FFS_ADJ_NBFREE 8 /* adjust number of free blocks */ #define FFS_ADJ_NIFREE 9 /* adjust number of free inodes */ #define FFS_ADJ_NFFREE 10 /* adjust number of free frags */ #define FFS_ADJ_NUMCLUSTERS 11 /* adjust number of free clusters */ #define FFS_SET_CWD 12 /* set current directory */ #define FFS_SET_DOTDOT 13 /* set inode number for ".." */ #define FFS_UNLINK 14 /* remove a name in the filesystem */ /* Was FFS_SET_INODE 15 */ /* Was FFS_SET_BUFOUTPUT 16 */ #define FFS_SET_SIZE 17 /* set inode size */ #define FFS_MAXID 17 /* number of valid ffs ids */ /* * Command structure passed in to the filesystem to adjust filesystem values. */ #define FFS_CMD_VERSION 0x19790518 /* version ID */ struct fsck_cmd { int32_t version; /* version of command structure */ int32_t handle; /* reference to filesystem to be changed */ int64_t value; /* inode or block number to be affected */ int64_t size; /* amount or range to be adjusted */ int64_t spare; /* reserved for future use */ }; /* * A recovery structure placed at the end of the boot block area by newfs * that can be used by fsck to search for alternate superblocks. */ struct fsrecovery { int32_t fsr_magic; /* magic number */ int32_t fsr_fsbtodb; /* fsbtodb and dbtofsb shift constant */ int32_t fsr_sblkno; /* offset of super-block in filesys */ int32_t fsr_fpg; /* blocks per group * fs_frag */ u_int32_t fsr_ncg; /* number of cylinder groups */ }; /* * 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_numclusters; /* number of free clusters */ int64_t cs_spare[3]; /* future expansion */ }; /* * Pointers to super block summary information. Placed in a separate * structure so there is just one pointer in the superblock. * * The pointers in this structure are used as follows: * fs_contigdirs references an array that tracks the creation of new * directories * fs_csp references a contiguous array of struct csum for * all cylinder groups * fs_maxcluster references an array of cluster sizes that is computed * as cylinder groups are inspected * fs_active is used when creating snapshots; it points to a bitmap * of cylinder groups for which the free-block bitmap has changed * since the snapshot operation began. */ struct fs_summary_info { uint8_t *si_contigdirs; /* (u) # of contig. allocated dirs */ struct csum *si_csp; /* (u) cg summary info buffer */ int32_t *si_maxcluster; /* (u) max cluster in each cyl group */ u_int *si_active; /* (u) used by snapshots to track fs */ }; #define fs_contigdirs fs_si->si_contigdirs #define fs_csp fs_si->si_csp #define fs_maxcluster fs_si->si_maxcluster #define fs_active fs_si->si_active /* * Super block for an FFS filesystem. */ struct fs { int32_t fs_firstfield; /* historic filesystem linked list, */ int32_t fs_unused_1; /* used for incore super blocks */ int32_t fs_sblkno; /* offset 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 */ u_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 */ u_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 */ 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 */ 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 */ 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 */ u_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 */ int8_t fs_clean; /* filesystem is clean flag */ int8_t fs_ronly; /* mounted read-only flag */ int8_t fs_old_flags; /* old FS_ flags */ u_char fs_fsmnt[MAXMNTLEN]; /* name mounted on */ u_char fs_volname[MAXVOLLEN]; /* volume name */ u_int64_t fs_swuid; /* system-wide uid */ int32_t fs_pad; /* due to alignment of fs_swuid */ /* 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 */ struct fs_summary_info *fs_si;/* In-core pointer to summary info */ int32_t fs_old_cpc; /* cyl per cycle in postbl */ int32_t fs_maxbsize; /* maximum blocking factor permitted */ int64_t fs_unrefs; /* number of unreferenced inodes */ int64_t fs_providersize; /* size of underlying GEOM provider */ int64_t fs_metaspace; /* size of area reserved for metadata */ int64_t fs_sparecon64[13]; /* old rotation block list head */ int64_t fs_sblockactualloc; /* byte offset of this superblock */ int64_t fs_sblockloc; /* byte offset of standard superblock */ struct csum_total fs_cstotal; /* (u) cylinder summary information */ ufs_time_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 */ ufs2_daddr_t fs_csaddr; /* blk addr of cyl grp summary area */ int64_t fs_pendingblocks; /* (u) blocks being freed */ u_int32_t fs_pendinginodes; /* (u) inodes being freed */ uint32_t fs_snapinum[FSMAXSNAP];/* list of snapshot inode numbers */ u_int32_t fs_avgfilesize; /* expected average file size */ u_int32_t fs_avgfpdir; /* expected # of files per directory */ int32_t fs_save_cgsize; /* save real cg size to use fs_bsize */ ufs_time_t fs_mtime; /* Last mount or fsck time. */ int32_t fs_sujfree; /* SUJ free list */ int32_t fs_sparecon32[21]; /* reserved for future constants */ u_int32_t fs_ckhash; /* if CK_SUPERBLOCK, its check-hash */ u_int32_t fs_metackhash; /* metadata check-hash, see CK_ below */ int32_t fs_flags; /* see FS_ flags below */ 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 */ 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 */ }; /* Sanity checking. */ #ifdef CTASSERT CTASSERT(sizeof(struct fs) == 1376); #endif /* * Filesystem identification */ #define FS_UFS1_MAGIC 0x011954 /* UFS1 fast filesystem magic number */ #define FS_UFS2_MAGIC 0x19540119 /* UFS2 fast filesystem magic number */ #define FS_BAD_MAGIC 0x19960408 /* UFS incomplete newfs magic number */ #define FS_42INODEFMT -1 /* 4.2BSD inode format */ #define FS_44INODEFMT 2 /* 4.4BSD inode format */ /* * Preference for optimization. */ #define FS_OPTTIME 0 /* minimize allocation time */ #define FS_OPTSPACE 1 /* minimize disk fragmentation */ /* * Filesystem flags. * * The FS_UNCLEAN flag is set by the kernel when the filesystem was * mounted with fs_clean set to zero. The FS_DOSOFTDEP flag indicates * that the filesystem should be managed by the soft updates code. * Note that the FS_NEEDSFSCK flag is set and cleared by the fsck * utility. It is set when background fsck finds an unexpected * inconsistency which requires a traditional foreground fsck to be * run. Such inconsistencies should only be found after an uncorrectable * disk error. The FS_NEEDSFSCK can also be set when a mounted filesystem * discovers an internal inconsistency such as freeing a freed inode. * A foreground fsck will clear the FS_NEEDSFSCK flag when it has * successfully cleaned up the filesystem. The kernel uses this * flag to enforce that inconsistent filesystems be mounted read-only. * * The FS_METACKHASH flag when set indicates that the kernel maintains * one or more check hashes. The actual set of supported check hashes * is stored in the fs_metackhash field. Kernels that do not support * check hashes clear the FS_METACKHASH flag to indicate that the * check hashes need to be rebuilt (by fsck) before they can be used. * * When a filesystem is mounted, any flags not included in FS_SUPPORTED * are cleared. This lets newer features know that the filesystem has * been run on an older version of the filesystem and thus that data * structures associated with those features are out-of-date and need * to be rebuilt. * * FS_ACLS indicates that POSIX.1e ACLs are administratively enabled * for the file system, so they should be loaded from extended attributes, * observed for access control purposes, and be administered by object * owners. FS_NFS4ACLS indicates that NFSv4 ACLs are administratively * enabled. This flag is mutually exclusive with FS_ACLS. FS_MULTILABEL * indicates that the TrustedBSD MAC Framework should attempt to back MAC * labels into extended attributes on the file system rather than maintain * a single mount label for all objects. */ #define FS_UNCLEAN 0x00000001 /* filesystem not clean at mount */ #define FS_DOSOFTDEP 0x00000002 /* filesystem using soft dependencies */ #define FS_NEEDSFSCK 0x00000004 /* filesystem needs sync fsck before mount */ #define FS_SUJ 0x00000008 /* Filesystem using softupdate journal */ #define FS_ACLS 0x00000010 /* file system has POSIX.1e ACLs enabled */ #define FS_MULTILABEL 0x00000020 /* file system is MAC multi-label */ #define FS_GJOURNAL 0x00000040 /* gjournaled file system */ #define FS_FLAGS_UPDATED 0x0000080 /* flags have been moved to new location */ #define FS_NFS4ACLS 0x00000100 /* file system has NFSv4 ACLs enabled */ #define FS_METACKHASH 0x00000200 /* kernel supports metadata check hashes */ #define FS_TRIM 0x00000400 /* issue BIO_DELETE for deleted blocks */ #define FS_SUPPORTED 0x00FFFFFF /* supported flags, others cleared at mount*/ /* * Things that we may someday support, but currently do not. * These flags are all cleared so we know if we ran on a kernel * that does not support them. */ #define FS_INDEXDIRS 0x01000000 /* kernel supports indexed directories */ #define FS_VARBLKSIZE 0x02000000 /* kernel supports variable block sizes */ #define FS_COOLOPT1 0x04000000 /* kernel supports cool option 1 */ #define FS_COOLOPT2 0x08000000 /* kernel supports cool option 2 */ #define FS_COOLOPT3 0x10000000 /* kernel supports cool option 3 */ #define FS_COOLOPT4 0x20000000 /* kernel supports cool option 4 */ #define FS_COOLOPT5 0x40000000 /* kernel supports cool option 5 */ #define FS_COOLOPT6 0x80000000 /* kernel supports cool option 6 */ /* * The fs_metackhash field indicates the types of metadata check-hash * that are maintained for a filesystem. Not all filesystems check-hash * all metadata. */ #define CK_SUPERBLOCK 0x0001 /* the superblock */ #define CK_CYLGRP 0x0002 /* the cylinder groups */ #define CK_INODE 0x0004 /* inodes */ #define CK_INDIR 0x0008 /* indirect blocks */ #define CK_DIR 0x0010 /* directory contents */ #define CK_SUPPORTED 0x0007 /* supported flags, others cleared at mount */ /* * The BX_FSPRIV buffer b_xflags are used to track types of data in buffers. */ #define BX_SUPERBLOCK 0x00010000 /* superblock */ #define BX_CYLGRP 0x00020000 /* cylinder groups */ #define BX_INODE 0x00040000 /* inodes */ #define BX_INDIR 0x00080000 /* indirect blocks */ #define BX_DIR 0x00100000 /* directory contents */ #define PRINT_UFS_BUF_XFLAGS "\20\25dir\24indir\23inode\22cylgrp\21superblock" /* * Macros to access bits in the fs_active array. */ #define ACTIVECGNUM(fs, cg) ((fs)->fs_active[(cg) / (NBBY * sizeof(int))]) #define ACTIVECGOFF(cg) (1 << ((cg) % (NBBY * sizeof(int)))) #define ACTIVESET(fs, cg) do { \ if ((fs)->fs_active) \ ACTIVECGNUM((fs), (cg)) |= ACTIVECGOFF((cg)); \ } while (0) #define ACTIVECLEAR(fs, cg) do { \ if ((fs)->fs_active) \ ACTIVECGNUM((fs), (cg)) &= ~ACTIVECGOFF((cg)); \ } while (0) /* * 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) + \ /* old btotoff */ (fs)->fs_old_cpg * sizeof(int32_t) + \ /* old boff */ (fs)->fs_old_cpg * sizeof(u_int16_t) + \ /* inode map */ howmany((fs)->fs_ipg, NBBY) + \ /* block map */ howmany((fs)->fs_fpg, NBBY) +\ /* if present */ ((fs)->fs_contigsumsize <= 0 ? 0 : \ /* cluster sum */ (fs)->fs_contigsumsize * sizeof(int32_t) + \ /* cluster map */ howmany(fragstoblks(fs, (fs)->fs_fpg), NBBY))) /* * 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 */ u_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 */ u_int32_t cg_ndblk; /* number of data blocks this cg */ struct csum cg_cs; /* cylinder summary information */ u_int32_t cg_rotor; /* position of last used block */ u_int32_t cg_frotor; /* position of last used frag */ u_int32_t cg_irotor; /* position of last used inode */ u_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 */ u_int32_t cg_iusedoff; /* (u_int8) used inode map */ u_int32_t cg_freeoff; /* (u_int8) free block map */ u_int32_t cg_nextfreeoff; /* (u_int8) next available space */ u_int32_t cg_clustersumoff; /* (u_int32) counts of avail clusters */ u_int32_t cg_clusteroff; /* (u_int8) free cluster map */ u_int32_t cg_nclusterblks; /* number of clusters this cg */ u_int32_t cg_niblk; /* number of inode blocks this cg */ u_int32_t cg_initediblk; /* last initialized inode */ u_int32_t cg_unrefs; /* number of unreferenced inodes */ int32_t cg_sparecon32[1]; /* reserved for future use */ u_int32_t cg_ckhash; /* check-hash of this cg */ ufs_time_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 */ }; /* * Macros for access to cylinder group array structures */ #define cg_chkmagic(cgp) ((cgp)->cg_magic == CG_MAGIC) #define cg_inosused(cgp) \ ((u_int8_t *)((u_int8_t *)(cgp) + (cgp)->cg_iusedoff)) #define cg_blksfree(cgp) \ ((u_int8_t *)((u_int8_t *)(cgp) + (cgp)->cg_freeoff)) #define cg_clustersfree(cgp) \ ((u_int8_t *)((u_int8_t *)(cgp) + (cgp)->cg_clusteroff)) #define cg_clustersum(cgp) \ ((int32_t *)((uintptr_t)(cgp) + (cgp)->cg_clustersumoff)) /* * Turn filesystem block numbers into disk block addresses. * This maps filesystem blocks to device size blocks. */ #define fsbtodb(fs, b) ((daddr_t)(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) (((ufs2_daddr_t)(fs)->fs_fpg) * (c)) #define cgdata(fs, c) (cgdmin(fs, c) + (fs)->fs_metaspace) /* data zone */ #define cgmeta(fs, c) (cgdmin(fs, c)) /* meta data */ #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) \ ((fs)->fs_magic == FS_UFS2_MAGIC ? cgbase(fs, c) : \ (cgbase(fs, c) + (fs)->fs_old_cgoffset * ((c) & ~((fs)->fs_old_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) (((ino_t)(x)) / (fs)->fs_ipg) #define ino_to_fsba(fs, x) \ ((ufs2_daddr_t)(cgimin(fs, ino_to_cg(fs, (ino_t)(x))) + \ (blkstofrags((fs), ((((ino_t)(x)) % (fs)->fs_ipg) / INOPB(fs)))))) #define ino_to_fsbo(fs, x) (((ino_t)(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))) /* * 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) /* Use this only when `blk' is known to be small, e.g., < UFS_NDADDR. */ #define smalllblktosize(fs, blk) /* calculates (blk * fs->fs_bsize) */ \ ((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) >= UFS_NDADDR || (ip)->i_size >= \ (uint64_t)smalllblktosize(fs, (lbn) + 1)) \ ? (fs)->fs_bsize \ : (fragroundup(fs, blkoff(fs, (ip)->i_size)))) #define sblksize(fs, size, lbn) \ (((lbn) >= UFS_NDADDR || (size) >= ((lbn) + 1) << (fs)->fs_bshift) \ ? (fs)->fs_bsize \ : (fragroundup(fs, blkoff(fs, (size))))) /* * Number of indirects in a filesystem block. */ #define NINDIR(fs) ((fs)->fs_nindir) /* * Indirect lbns are aligned on UFS_NDADDR addresses where single indirects * are the negated address of the lowest lbn reachable, double indirects * are this lbn - 1 and triple indirects are this lbn - 2. This yields * an unusual bit order to determine level. */ static inline int lbn_level(ufs_lbn_t lbn) { if (lbn >= 0) return 0; switch (lbn & 0x3) { case 0: return (0); case 1: break; case 2: return (2); case 3: return (1); default: break; } return (-1); } static inline ufs_lbn_t lbn_offset(struct fs *fs, int level) { ufs_lbn_t res; for (res = 1; level > 0; level--) res *= NINDIR(fs); return (res); } /* * Number of inodes in a secondary storage block/fragment. */ #define INOPB(fs) ((fs)->fs_inopb) #define INOPF(fs) ((fs)->fs_inopb >> (fs)->fs_fragshift) /* * Softdep journal record format. */ #define JOP_ADDREF 1 /* Add a reference to an inode. */ #define JOP_REMREF 2 /* Remove a reference from an inode. */ #define JOP_NEWBLK 3 /* Allocate a block. */ #define JOP_FREEBLK 4 /* Free a block or a tree of blocks. */ #define JOP_MVREF 5 /* Move a reference from one off to another. */ #define JOP_TRUNC 6 /* Partial truncation record. */ #define JOP_SYNC 7 /* fsync() complete record. */ #define JREC_SIZE 32 /* Record and segment header size. */ #define SUJ_MIN (4 * 1024 * 1024) /* Minimum journal size */ #define SUJ_MAX (32 * 1024 * 1024) /* Maximum journal size */ #define SUJ_FILE ".sujournal" /* Journal file name */ /* * Size of the segment record header. There is at most one for each disk * block in the journal. The segment header is followed by an array of * records. fsck depends on the first element in each record being 'op' * and the second being 'ino'. Segments may span multiple disk blocks but * the header is present on each. */ struct jsegrec { uint64_t jsr_seq; /* Our sequence number */ uint64_t jsr_oldest; /* Oldest valid sequence number */ uint16_t jsr_cnt; /* Count of valid records */ uint16_t jsr_blocks; /* Count of device bsize blocks. */ uint32_t jsr_crc; /* 32bit crc of the valid space */ ufs_time_t jsr_time; /* timestamp for mount instance */ }; /* * Reference record. Records a single link count modification. */ struct jrefrec { uint32_t jr_op; uint32_t jr_ino; uint32_t jr_parent; uint16_t jr_nlink; uint16_t jr_mode; int64_t jr_diroff; uint64_t jr_unused; }; /* * Move record. Records a reference moving within a directory block. The * nlink is unchanged but we must search both locations. */ struct jmvrec { uint32_t jm_op; uint32_t jm_ino; uint32_t jm_parent; uint16_t jm_unused; int64_t jm_oldoff; int64_t jm_newoff; }; /* * Block record. A set of frags or tree of blocks starting at an indirect are * freed or a set of frags are allocated. */ struct jblkrec { uint32_t jb_op; uint32_t jb_ino; ufs2_daddr_t jb_blkno; ufs_lbn_t jb_lbn; uint16_t jb_frags; uint16_t jb_oldfrags; uint32_t jb_unused; }; /* * Truncation record. Records a partial truncation so that it may be * completed at check time. Also used for sync records. */ struct jtrncrec { uint32_t jt_op; uint32_t jt_ino; int64_t jt_size; uint32_t jt_extsize; uint32_t jt_pad[3]; }; union jrec { struct jsegrec rec_jsegrec; struct jrefrec rec_jrefrec; struct jmvrec rec_jmvrec; struct jblkrec rec_jblkrec; struct jtrncrec rec_jtrncrec; }; #ifdef CTASSERT CTASSERT(sizeof(struct jsegrec) == JREC_SIZE); CTASSERT(sizeof(struct jrefrec) == JREC_SIZE); CTASSERT(sizeof(struct jmvrec) == JREC_SIZE); CTASSERT(sizeof(struct jblkrec) == JREC_SIZE); CTASSERT(sizeof(struct jtrncrec) == JREC_SIZE); CTASSERT(sizeof(union jrec) == JREC_SIZE); #endif extern int inside[], around[]; extern u_char *fragtbl[]; /* * IOCTLs used for filesystem write suspension. */ #define UFSSUSPEND _IOW('U', 1, fsid_t) #define UFSRESUME _IO('U', 2) #endif diff --git a/usr.sbin/fstyp/ufs.c b/usr.sbin/fstyp/ufs.c index e4de1283e769..69ce90d600f2 100644 --- a/usr.sbin/fstyp/ufs.c +++ b/usr.sbin/fstyp/ufs.c @@ -1,67 +1,65 @@ /*- * Copyright (c) 2002, 2003 Gordon Tetlow * Copyright (c) 2006 Pawel Jakub Dawidek * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include "fstyp.h" int fstyp_ufs(FILE *fp, char *label, size_t labelsize) { struct fs *fs; - switch (sbget(fileno(fp), &fs, STDSB)) { + switch (sbget(fileno(fp), &fs, UFS_STDSB, UFS_NOCSUM)) { case 0: strlcpy(label, fs->fs_volname, labelsize); - free(fs->fs_csp); - free(fs->fs_si); free(fs); return (0); case ENOENT: /* Cannot find file system superblock */ return (1); default: /* Unable to read file system superblock */ return (1); } } diff --git a/usr.sbin/quot/quot.c b/usr.sbin/quot/quot.c index fa1ee1c65fe7..7e84dd79e148 100644 --- a/usr.sbin/quot/quot.c +++ b/usr.sbin/quot/quot.c @@ -1,634 +1,632 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (C) 1991, 1994 Wolfgang Solfrank. * Copyright (C) 1991, 1994 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* some flags of what to do: */ static char estimate; static char count; static char unused; static void (*func)(int, struct fs *, char *); static long blocksize; static char *header; static int headerlen; static union dinode *get_inode(int, struct fs *, ino_t); static int virtualblocks(struct fs *, union dinode *); static int isfree(struct fs *, union dinode *); static void inituser(void); static void usrrehash(void); static struct user *user(uid_t); static int cmpusers(const void *, const void *); static void uses(uid_t, daddr_t, time_t); static void initfsizes(void); static void dofsizes(int, struct fs *, char *); static void douser(int, struct fs *, char *); static void donames(int, struct fs *, char *); static void usage(void); static void quot(char *, char *); /* * Original BSD quot doesn't round to number of frags/blocks, * doesn't account for indirection blocks and gets it totally * wrong if the size is a multiple of the blocksize. * The new code always counts the number of 512 byte blocks * instead of the number of kilobytes and converts them to * kByte when done (on request). * * Due to the size of modern disks, we must cast intermediate * values to 64 bits to prevent potential overflows. */ #ifdef COMPAT #define SIZE(n) (n) #else #define SIZE(n) ((int)(((quad_t)(n) * 512 + blocksize - 1)/blocksize)) #endif #define INOCNT(fs) ((fs)->fs_ipg) #define INOSZ(fs) \ (((fs)->fs_magic == FS_UFS1_MAGIC ? sizeof(struct ufs1_dinode) : \ sizeof(struct ufs2_dinode)) * INOCNT(fs)) union dinode { struct ufs1_dinode dp1; struct ufs2_dinode dp2; }; #define DIP(fs, dp, field) \ (((fs)->fs_magic == FS_UFS1_MAGIC) ? \ (dp)->dp1.field : (dp)->dp2.field) static union dinode * get_inode(int fd, struct fs *super, ino_t ino) { static caddr_t ipbuf; static struct cg *cgp; static ino_t last; static int cg; struct ufs2_dinode *di2; if (fd < 0) { /* flush cache */ if (ipbuf) { free(ipbuf); ipbuf = 0; if (super != NULL && super->fs_magic == FS_UFS2_MAGIC) { free(cgp); cgp = 0; } } return 0; } if (!ipbuf || ino < last || ino >= last + INOCNT(super)) { if (super->fs_magic == FS_UFS2_MAGIC && (!cgp || cg != ino_to_cg(super, ino))) { cg = ino_to_cg(super, ino); if (!cgp && !(cgp = malloc(super->fs_cgsize))) errx(1, "allocate cg"); if (lseek(fd, (off_t)cgtod(super, cg) << super->fs_fshift, 0) < 0) err(1, "lseek cg"); if (read(fd, cgp, super->fs_cgsize) != super->fs_cgsize) err(1, "read cg"); if (!cg_chkmagic(cgp)) errx(1, "cg has bad magic"); } if (!ipbuf && !(ipbuf = malloc(INOSZ(super)))) errx(1, "allocate inodes"); last = rounddown(ino, INOCNT(super)); if (lseek(fd, (off_t)ino_to_fsba(super, last) << super->fs_fshift, 0) < (off_t)0 || read(fd, ipbuf, INOSZ(super)) != (ssize_t)INOSZ(super)) err(1, "read inodes"); } if (super->fs_magic == FS_UFS1_MAGIC) return ((union dinode *) &((struct ufs1_dinode *)ipbuf)[ino % INOCNT(super)]); di2 = &((struct ufs2_dinode *)ipbuf)[ino % INOCNT(super)]; /* If the inode is unused, it might be unallocated too, so zero it. */ if (isclr(cg_inosused(cgp), ino % super->fs_ipg)) bzero(di2, sizeof (*di2)); return ((union dinode *)di2); } #ifdef COMPAT #define actualblocks(fs, dp) (DIP(fs, dp, di_blocks) / 2) #else #define actualblocks(fs, dp) DIP(fs, dp, di_blocks) #endif static int virtualblocks(struct fs *super, union dinode *dp) { off_t nblk, sz; sz = DIP(super, dp, di_size); #ifdef COMPAT if (lblkno(super,sz) >= UFS_NDADDR) { nblk = blkroundup(super,sz); if (sz == nblk) nblk += super->fs_bsize; } return sz / 1024; #else /* COMPAT */ if (lblkno(super,sz) >= UFS_NDADDR) { nblk = blkroundup(super,sz); sz = lblkno(super,nblk); sz = (sz - UFS_NDADDR + NINDIR(super) - 1) / NINDIR(super); while (sz > 0) { nblk += sz * super->fs_bsize; /* sz - 1 rounded up */ sz = (sz - 1 + NINDIR(super) - 1) / NINDIR(super); } } else nblk = fragroundup(super,sz); return nblk / 512; #endif /* COMPAT */ } static int isfree(struct fs *super, union dinode *dp) { #ifdef COMPAT return (DIP(super, dp, di_mode) & IFMT) == 0; #else /* COMPAT */ switch (DIP(super, dp, di_mode) & IFMT) { case IFIFO: case IFLNK: /* should check FASTSYMLINK? */ case IFDIR: case IFREG: return 0; case IFCHR: case IFBLK: case IFSOCK: case IFWHT: case 0: return 1; default: errx(1, "unknown IFMT 0%o", DIP(super, dp, di_mode) & IFMT); } #endif } static struct user { uid_t uid; char *name; daddr_t space; long count; daddr_t spc30; daddr_t spc60; daddr_t spc90; } *users; static int nusers; static void inituser(void) { int i; struct user *usr; if (!nusers) { nusers = 8; if (!(users = (struct user *)calloc(nusers,sizeof(struct user)))) errx(1, "allocate users"); } else { for (usr = users, i = nusers; --i >= 0; usr++) { usr->space = usr->spc30 = usr->spc60 = usr->spc90 = 0; usr->count = 0; } } } static void usrrehash(void) { int i; struct user *usr, *usrn; struct user *svusr; svusr = users; nusers <<= 1; if (!(users = (struct user *)calloc(nusers,sizeof(struct user)))) errx(1, "allocate users"); for (usr = svusr, i = nusers >> 1; --i >= 0; usr++) { for (usrn = users + (usr->uid&(nusers - 1)); usrn->name; usrn--) { if (usrn <= users) usrn = users + nusers; } *usrn = *usr; } } static struct user * user(uid_t uid) { struct user *usr; int i; struct passwd *pwd; while (1) { for (usr = users + (uid&(nusers - 1)), i = nusers; --i >= 0; usr--) { if (!usr->name) { usr->uid = uid; if (!(pwd = getpwuid(uid))) { if ((usr->name = (char *)malloc(7))) sprintf(usr->name,"#%d",uid); } else { if ((usr->name = (char *) malloc(strlen(pwd->pw_name) + 1))) strcpy(usr->name,pwd->pw_name); } if (!usr->name) errx(1, "allocate users"); return usr; } else if (usr->uid == uid) return usr; if (usr <= users) usr = users + nusers; } usrrehash(); } } static int cmpusers(const void *v1, const void *v2) { const struct user *u1, *u2; u1 = (const struct user *)v1; u2 = (const struct user *)v2; return u2->space - u1->space; } #define sortusers(users) (qsort((users),nusers,sizeof(struct user), \ cmpusers)) static void uses(uid_t uid, daddr_t blks, time_t act) { static time_t today; struct user *usr; if (!today) time(&today); usr = user(uid); usr->count++; usr->space += blks; if (today - act > 90L * 24L * 60L * 60L) usr->spc90 += blks; if (today - act > 60L * 24L * 60L * 60L) usr->spc60 += blks; if (today - act > 30L * 24L * 60L * 60L) usr->spc30 += blks; } #ifdef COMPAT #define FSZCNT 500 #else #define FSZCNT 512 #endif struct fsizes { struct fsizes *fsz_next; daddr_t fsz_first, fsz_last; ino_t fsz_count[FSZCNT]; daddr_t fsz_sz[FSZCNT]; } *fsizes; static void initfsizes(void) { struct fsizes *fp; int i; for (fp = fsizes; fp; fp = fp->fsz_next) { for (i = FSZCNT; --i >= 0;) { fp->fsz_count[i] = 0; fp->fsz_sz[i] = 0; } } } static void dofsizes(int fd, struct fs *super, char *name) { ino_t inode, maxino; union dinode *dp; daddr_t sz, ksz; struct fsizes *fp, **fsp; int i; maxino = super->fs_ncg * super->fs_ipg - 1; #ifdef COMPAT if (!(fsizes = (struct fsizes *)malloc(sizeof(struct fsizes)))) errx(1, "allocate fsize structure"); #endif /* COMPAT */ for (inode = 0; inode < maxino; inode++) { errno = 0; if ((dp = get_inode(fd,super,inode)) #ifdef COMPAT && ((DIP(super, dp, di_mode) & IFMT) == IFREG || (DIP(super, dp, di_mode) & IFMT) == IFDIR) #else /* COMPAT */ && !isfree(super, dp) #endif /* COMPAT */ ) { sz = estimate ? virtualblocks(super, dp) : actualblocks(super, dp); #ifdef COMPAT if (sz >= FSZCNT) { fsizes->fsz_count[FSZCNT-1]++; fsizes->fsz_sz[FSZCNT-1] += sz; } else { fsizes->fsz_count[sz]++; fsizes->fsz_sz[sz] += sz; } #else /* COMPAT */ ksz = SIZE(sz); for (fsp = &fsizes; (fp = *fsp); fsp = &fp->fsz_next) { if (ksz < fp->fsz_last) break; } if (!fp || ksz < fp->fsz_first) { if (!(fp = (struct fsizes *) malloc(sizeof(struct fsizes)))) errx(1, "allocate fsize structure"); fp->fsz_next = *fsp; *fsp = fp; fp->fsz_first = rounddown(ksz, FSZCNT); fp->fsz_last = fp->fsz_first + FSZCNT; for (i = FSZCNT; --i >= 0;) { fp->fsz_count[i] = 0; fp->fsz_sz[i] = 0; } } fp->fsz_count[ksz % FSZCNT]++; fp->fsz_sz[ksz % FSZCNT] += sz; #endif /* COMPAT */ } else if (errno) { err(1, "%s", name); } } sz = 0; for (fp = fsizes; fp; fp = fp->fsz_next) { for (i = 0; i < FSZCNT; i++) { if (fp->fsz_count[i]) printf("%jd\t%jd\t%d\n", (intmax_t)(fp->fsz_first + i), (intmax_t)fp->fsz_count[i], SIZE(sz += fp->fsz_sz[i])); } } } static void douser(int fd, struct fs *super, char *name) { ino_t inode, maxino; struct user *usr, *usrs; union dinode *dp; int n; maxino = super->fs_ncg * super->fs_ipg - 1; for (inode = 0; inode < maxino; inode++) { errno = 0; if ((dp = get_inode(fd,super,inode)) && !isfree(super, dp)) uses(DIP(super, dp, di_uid), estimate ? virtualblocks(super, dp) : actualblocks(super, dp), DIP(super, dp, di_atime)); else if (errno) { err(1, "%s", name); } } if (!(usrs = (struct user *)malloc(nusers * sizeof(struct user)))) errx(1, "allocate users"); bcopy(users,usrs,nusers * sizeof(struct user)); sortusers(usrs); for (usr = usrs, n = nusers; --n >= 0 && usr->count; usr++) { printf("%5d",SIZE(usr->space)); if (count) printf("\t%5ld",usr->count); printf("\t%-8s",usr->name); if (unused) printf("\t%5d\t%5d\t%5d", SIZE(usr->spc30), SIZE(usr->spc60), SIZE(usr->spc90)); printf("\n"); } free(usrs); } static void donames(int fd, struct fs *super, char *name) { int c; ino_t maxino; uintmax_t inode; union dinode *dp; maxino = super->fs_ncg * super->fs_ipg - 1; /* first skip the name of the filesystem */ while ((c = getchar()) != EOF && (c < '0' || c > '9')) while ((c = getchar()) != EOF && c != '\n'); ungetc(c,stdin); while (scanf("%ju", &inode) == 1) { if (inode > maxino) { warnx("illegal inode %ju", inode); return; } errno = 0; if ((dp = get_inode(fd,super,inode)) && !isfree(super, dp)) { printf("%s\t",user(DIP(super, dp, di_uid))->name); /* now skip whitespace */ while ((c = getchar()) == ' ' || c == '\t'); /* and print out the remainder of the input line */ while (c != EOF && c != '\n') { putchar(c); c = getchar(); } putchar('\n'); } else { if (errno) { err(1, "%s", name); } /* skip this line */ while ((c = getchar()) != EOF && c != '\n'); } if (c == EOF) break; } } static void usage(void) { #ifdef COMPAT fprintf(stderr,"usage: quot [-nfcvha] [filesystem ...]\n"); #else /* COMPAT */ fprintf(stderr,"usage: quot [-acfhknv] [filesystem ...]\n"); #endif /* COMPAT */ exit(1); } void quot(char *name, char *mp) { int fd; struct fs *fs; get_inode(-1, NULL, 0); /* flush cache */ inituser(); initfsizes(); if ((fd = open(name,0)) < 0) { warn("%s", name); close(fd); return; } - switch (sbget(fd, &fs, STDSB)) { + switch (errno = sbget(fd, &fs, UFS_STDSB, UFS_NOCSUM)) { case 0: break; case ENOENT: warn("Cannot find file system superblock"); close(fd); return; default: warn("Unable to read file system superblock"); close(fd); return; } printf("%s:",name); if (mp) printf(" (%s)",mp); putchar('\n'); (*func)(fd, fs, name); - free(fs->fs_csp); - free(fs->fs_si); free(fs); close(fd); } int main(int argc, char *argv[]) { char all = 0; struct statfs *mp; struct fstab *fs; int cnt; func = douser; #ifndef COMPAT header = getbsize(&headerlen,&blocksize); #endif while (--argc > 0 && **++argv == '-') { while (*++*argv) { switch (**argv) { case 'n': func = donames; break; case 'c': func = dofsizes; break; case 'a': all = 1; break; case 'f': count = 1; break; case 'h': estimate = 1; break; #ifndef COMPAT case 'k': blocksize = 1024; break; #endif /* COMPAT */ case 'v': unused = 1; break; default: usage(); } } } if (all) { cnt = getmntinfo(&mp,MNT_NOWAIT); for (; --cnt >= 0; mp++) { if (!strncmp(mp->f_fstypename, "ufs", MFSNAMELEN)) quot(mp->f_mntfromname, mp->f_mntonname); } } while (--argc >= 0) { if ((fs = getfsfile(*argv)) != NULL) quot(fs->fs_spec, 0); else quot(*argv,0); argv++; } return 0; }