aboutsummaryrefslogtreecommitdiff
path: root/sys/ufs/lfs
diff options
context:
space:
mode:
Diffstat (limited to 'sys/ufs/lfs')
-rw-r--r--sys/ufs/lfs/README139
-rw-r--r--sys/ufs/lfs/TODO116
-rw-r--r--sys/ufs/lfs/lfs.h353
-rw-r--r--sys/ufs/lfs/lfs_alloc.c251
-rw-r--r--sys/ufs/lfs/lfs_balloc.c136
-rw-r--r--sys/ufs/lfs/lfs_bio.c187
-rw-r--r--sys/ufs/lfs/lfs_cksum.c58
-rw-r--r--sys/ufs/lfs/lfs_debug.c137
-rw-r--r--sys/ufs/lfs/lfs_extern.h106
-rw-r--r--sys/ufs/lfs/lfs_inode.c359
-rw-r--r--sys/ufs/lfs/lfs_segment.c1111
-rw-r--r--sys/ufs/lfs/lfs_subr.c184
-rw-r--r--sys/ufs/lfs/lfs_syscalls.c562
-rw-r--r--sys/ufs/lfs/lfs_vfsops.c573
-rw-r--r--sys/ufs/lfs/lfs_vnops.c487
15 files changed, 4759 insertions, 0 deletions
diff --git a/sys/ufs/lfs/README b/sys/ufs/lfs/README
new file mode 100644
index 000000000000..724b18fb9ea5
--- /dev/null
+++ b/sys/ufs/lfs/README
@@ -0,0 +1,139 @@
+# @(#)README 8.1 (Berkeley) 6/11/93
+
+The file system is reasonably stable, but incomplete. There are
+places where cleaning performance can be improved dramatically (see
+comments in lfs_syscalls.c). For details on the implementation,
+performance and why garbage collection always wins, see Dr. Margo
+Seltzer's thesis available for anonymous ftp from toe.cs.berkeley.edu,
+in the directory pub/personal/margo/thesis.ps.Z, or the January 1993
+USENIX paper.
+
+Missing Functionality:
+ Multiple block sizes and/or fragments are not yet implemented.
+
+----------
+The disk is laid out in segments. The first segment starts 8K into the
+disk (the first 8K is used for boot information). Each segment is composed
+of the following:
+
+ An optional super block
+ One or more groups of:
+ segment summary
+ 0 or more data blocks
+ 0 or more inode blocks
+
+The segment summary and inode/data blocks start after the super block (if
+present), and grow toward the end of the segment.
+
+ _______________________________________________
+ | | | | |
+ | summary | data/inode | summary | data/inode |
+ | block | blocks | block | blocks | ...
+ |_________|____________|_________|____________|
+
+The data/inode blocks following a summary block are described by the
+summary block. In order to permit the segment to be written in any order
+and in a forward direction only, a checksum is calculated across the
+blocks described by the summary. Additionally, the summary is checksummed
+and timestamped. Both of these are intended for recovery; the former is
+to make it easy to determine that it *is* a summary block and the latter
+is to make it easy to determine when recovery is finished for partially
+written segments. These checksums are also used by the cleaner.
+
+ Summary block (detail)
+ ________________
+ | sum cksum |
+ | data cksum |
+ | next segment |
+ | timestamp |
+ | FINFO count |
+ | inode count |
+ | flags |
+ |______________|
+ | FINFO-1 | 0 or more file info structures, identifying the
+ | . | blocks in the segment.
+ | . |
+ | . |
+ | FINFO-N |
+ | inode-N |
+ | . |
+ | . |
+ | . | 0 or more inode daddr_t's, identifying the inode
+ | inode-1 | blocks in the segment.
+ |______________|
+
+Inode blocks are blocks of on-disk inodes in the same format as those in
+the FFS. However, spare[0] contains the inode number of the inode so we
+can find a particular inode on a page. They are packed page_size /
+sizeof(inode) to a block. Data blocks are exactly as in the FFS. Both
+inodes and data blocks move around the file system at will.
+
+The file system is described by a super-block which is replicated and
+occurs as the first block of the first and other segments. (The maximum
+number of super-blocks is MAXNUMSB). Each super-block maintains a list
+of the disk addresses of all the super-blocks. The super-block maintains
+a small amount of checkpoint information, essentially just enough to find
+the inode for the IFILE (fs->lfs_idaddr).
+
+The IFILE is visible in the file system, as inode number IFILE_INUM. It
+contains information shared between the kernel and various user processes.
+
+ Ifile (detail)
+ ________________
+ | cleaner info | Cleaner information per file system. (Page
+ | | granularity.)
+ |______________|
+ | segment | Space available and last modified times per
+ | usage table | segment. (Page granularity.)
+ |______________|
+ | IFILE-1 | Per inode status information: current version #,
+ | . | if currently allocated, last access time and
+ | . | current disk address of containing inode block.
+ | . | If current disk address is LFS_UNUSED_DADDR, the
+ | IFILE-N | inode is not in use, and it's on the free list.
+ |______________|
+
+
+First Segment at Creation Time:
+_____________________________________________________________
+| | | | | | | |
+| 8K pad | Super | summary | inode | ifile | root | l + f |
+| | block | | block | | dir | dir |
+|________|_______|_________|_______|_______|_______|_______|
+ ^
+ Segment starts here.
+
+Some differences from the Sprite LFS implementation.
+
+1. The LFS implementation placed the ifile metadata and the super block
+ at fixed locations. This implementation replicates the super block
+ and puts each at a fixed location. The checkpoint data is divided into
+ two parts -- just enough information to find the IFILE is stored in
+ two of the super blocks, although it is not toggled between them as in
+ the Sprite implementation. (This was deliberate, to avoid a single
+ point of failure.) The remaining checkpoint information is treated as
+ a regular file, which means that the cleaner info, the segment usage
+ table and the ifile meta-data are stored in normal log segments.
+ (Tastes great, less filling...)
+
+2. The segment layout is radically different in Sprite; this implementation
+ uses something a lot like network framing, where data/inode blocks are
+ written asynchronously, and a checksum is used to validate any set of
+ summary and data/inode blocks. Sprite writes summary blocks synchronously
+ after the data/inode blocks have been written and the existence of the
+ summary block validates the data/inode blocks. This permits us to write
+ everything contiguously, even partial segments and their summaries, whereas
+ Sprite is forced to seek (from the end of the data inode to the summary
+ which lives at the end of the segment). Additionally, writing the summary
+ synchronously should cost about 1/2 a rotation per summary.
+
+3. Sprite LFS distinguishes between different types of blocks in the segment.
+ Other than inode blocks and data blocks, we don't.
+
+4. Sprite LFS traverses the IFILE looking for free blocks. We maintain a
+ free list threaded through the IFILE entries.
+
+5. The cleaner runs in user space, as opposed to kernel space. It shares
+ information with the kernel by reading/writing the IFILE and through
+ cleaner specific system calls.
+
diff --git a/sys/ufs/lfs/TODO b/sys/ufs/lfs/TODO
new file mode 100644
index 000000000000..ace8f5eaef6c
--- /dev/null
+++ b/sys/ufs/lfs/TODO
@@ -0,0 +1,116 @@
+# @(#)TODO 8.1 (Berkeley) 6/11/93
+
+NOTE: Changed the lookup on a page of inodes to search from the back
+in case the same inode gets written twice on the same page.
+
+Make sure that if you are writing a file, but not all the blocks
+make it into a single segment, that you do not write the inode in
+that segment.
+
+Keith:
+ Why not delete the lfs_bmapv call, just mark everything dirty
+ that isn't deleted/truncated? Get some numbers about
+ what percentage of the stuff that the cleaner thinks
+ might be live is live. If it's high, get rid of lfs_bmapv.
+
+ There is a nasty problem in that it may take *more* room to write
+ the data to clean a segment than is returned by the new segment
+ because of indirect blocks in segment 2 being dirtied by the data
+ being copied into the log from segment 1. The suggested solution
+ at this point is to detect it when we have no space left on the
+ filesystem, write the extra data into the last segment (leaving
+ no clean ones), make it a checkpoint and shut down the file system
+ for fixing by a utility reading the raw partition. Argument is
+ that this should never happen and is practically impossible to fix
+ since the cleaner would have to theoretically build a model of the
+ entire filesystem in memory to detect the condition occurring.
+ A file coalescing cleaner will help avoid the problem, and one
+ that reads/writes from the raw disk could fix it.
+
+DONE Currently, inodes are being flushed to disk synchronously upon
+ creation -- see ufs_makeinode. However, only the inode
+ is flushed, the directory "name" is written using VOP_BWRITE,
+ so it's not synchronous. Possible solutions: 1: get some
+ ordering in the writes so that inode/directory entries get
+ stuffed into the same segment. 2: do both synchronously
+ 3: add Mendel's information into the stream so we log
+ creation/deletion of inodes. 4: do some form of partial
+ segment when changing the inode (creation/deletion/rename).
+DONE Fix i_block increment for indirect blocks.
+ If the file system is tar'd, extracted on top of another LFS, the
+ IFILE ain't worth diddly. Is the cleaner writing the IFILE?
+ If not, let's make it read-only.
+DONE Delete unnecessary source from utils in main-line source tree.
+DONE Make sure that we're counting meta blocks in the inode i_block count.
+ Overlap the version and nextfree fields in the IFILE
+DONE Vinvalbuf (Kirk):
+ Why writing blocks that are no longer useful?
+ Are the semantics of close such that blocks have to be flushed?
+ How specify in the buf chain the blocks that don't need
+ to be written? (Different numbering of indirect blocks.)
+
+Margo:
+ Change so that only search one sector of inode block file for the
+ inode by using sector addresses in the ifile instead of
+ logical disk addresses.
+ Fix the use of the ifile version field to use the generation
+ number instead.
+DONE Unmount; not doing a bgetvp (VHOLD) in lfs_newbuf call.
+DONE Document in the README file where the checkpoint information is
+ on disk.
+ Variable block sizes (Margo/Keith).
+ Switch the byte accounting to sector accounting.
+DONE Check lfs.h and make sure that the #defines/structures are all
+ actually needed.
+DONE Add a check in lfs_segment.c so that if the segment is empty,
+ we don't write it.
+ Need to keep vnode v_numoutput up to date for pending writes?
+DONE USENIX paper (Carl/Margo).
+
+
+Evelyn:
+ lfsck: If delete a file that's being executed, the version number
+ isn't updated, and lfsck has to figure this out; case is the same as if have an inode that no directory references,
+ so the file should be reattached into lost+found.
+ Recovery/fsck.
+
+Carl:
+ Investigate: clustering of reads (if blocks in the segment are ordered,
+ should read them all) and writes (McVoy paper).
+ Investigate: should the access time be part of the IFILE:
+ pro: theoretically, saves disk writes
+ con: cacheing inodes should obviate this advantage
+ the IFILE is already humongous
+ Cleaner.
+ Port to OSF/1 (Carl/Keith).
+ Currently there's no notion of write error checking.
+ + Failed data/inode writes should be rescheduled (kernel level
+ bad blocking).
+ + Failed superblock writes should cause selection of new
+ superblock for checkpointing.
+
+FUTURE FANTASIES: ============
+
++ unrm, versioning
++ transactions
++ extended cleaner policies (hot/cold data, data placement)
+
+==============================
+Problem with the concept of multiple buffer headers referencing the segment:
+Positives:
+ Don't lock down 1 segment per file system of physical memory.
+ Don't copy from buffers to segment memory.
+ Don't tie down the bus to transfer 1M.
+ Works on controllers supporting less than large transfers.
+ Disk can start writing immediately instead of waiting 1/2 rotation
+ and the full transfer.
+Negatives:
+ Have to do segment write then segment summary write, since the latter
+ is what verifies that the segment is okay. (Is there another way
+ to do this?)
+==============================
+
+The algorithm for selecting the disk addresses of the super-blocks
+has to be available to the user program which checks the file system.
+
+(Currently in newfs, becomes a common subroutine.)
diff --git a/sys/ufs/lfs/lfs.h b/sys/ufs/lfs/lfs.h
new file mode 100644
index 000000000000..87b8c22ccc02
--- /dev/null
+++ b/sys/ufs/lfs/lfs.h
@@ -0,0 +1,353 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ *
+ * @(#)lfs.h 8.3 (Berkeley) 9/23/93
+ */
+
+#define LFS_LABELPAD 8192 /* LFS label size */
+#define LFS_SBPAD 8192 /* LFS superblock size */
+
+/*
+ * XXX
+ * This is a kluge and NEEDS to go away.
+ *
+ * Right now, ufs code handles most of the calls for directory operations
+ * such as create, mkdir, link, etc. As a result VOP_UPDATE is being
+ * called with waitfor set (since ffs does these things synchronously).
+ * Since LFS does not want to do these synchronously, we treat the last
+ * argument to lfs_update as a set of flags. If LFS_SYNC is set, then
+ * the update should be synchronous, if not, do it asynchronously.
+ * Unfortunately, this means that LFS won't work with NFS yet because
+ * NFS goes through paths that will make normal calls to ufs which will
+ * call lfs with a last argument of 1.
+ */
+#define LFS_SYNC 0x02
+
+/* On-disk and in-memory checkpoint segment usage structure. */
+typedef struct segusage SEGUSE;
+struct segusage {
+ u_long su_nbytes; /* number of live bytes */
+ u_long su_lastmod; /* SEGUSE last modified timestamp */
+ u_short su_nsums; /* number of summaries in segment */
+ u_short su_ninos; /* number of inode blocks in seg */
+#define SEGUSE_ACTIVE 0x1 /* segment is currently being written */
+#define SEGUSE_DIRTY 0x2 /* segment has data in it */
+#define SEGUSE_SUPERBLOCK 0x4 /* segment contains a superblock */
+ u_long su_flags;
+};
+
+#define SEGUPB(fs) (1 << (fs)->lfs_sushift)
+#define SEGTABSIZE_SU(fs) \
+ (((fs)->lfs_nseg + SEGUPB(fs) - 1) >> (fs)->lfs_sushift)
+
+/* On-disk file information. One per file with data blocks in the segment. */
+typedef struct finfo FINFO;
+struct finfo {
+ u_long fi_nblocks; /* number of blocks */
+ u_long fi_version; /* version number */
+ u_long fi_ino; /* inode number */
+ long fi_blocks[1]; /* array of logical block numbers */
+};
+
+/* On-disk and in-memory super block. */
+struct lfs {
+#define LFS_MAGIC 0x070162
+ u_long lfs_magic; /* magic number */
+#define LFS_VERSION 1
+ u_long lfs_version; /* version number */
+
+ u_long lfs_size; /* number of blocks in fs */
+ u_long lfs_ssize; /* number of blocks per segment */
+ u_long lfs_dsize; /* number of disk blocks in fs */
+ u_long lfs_bsize; /* file system block size */
+ u_long lfs_fsize; /* size of frag blocks in fs */
+ u_long lfs_frag; /* number of frags in a block in fs */
+
+/* Checkpoint region. */
+ ino_t lfs_free; /* start of the free list */
+ u_long lfs_bfree; /* number of free disk blocks */
+ u_long lfs_nfiles; /* number of allocated inodes */
+ long lfs_avail; /* blocks available for writing */
+ u_long lfs_uinodes; /* inodes in cache not yet on disk */
+ daddr_t lfs_idaddr; /* inode file disk address */
+ ino_t lfs_ifile; /* inode file inode number */
+ daddr_t lfs_lastseg; /* address of last segment written */
+ daddr_t lfs_nextseg; /* address of next segment to write */
+ daddr_t lfs_curseg; /* current segment being written */
+ daddr_t lfs_offset; /* offset in curseg for next partial */
+ daddr_t lfs_lastpseg; /* address of last partial written */
+ u_long lfs_tstamp; /* time stamp */
+
+/* These are configuration parameters. */
+ u_long lfs_minfree; /* minimum percentage of free blocks */
+
+/* These fields can be computed from the others. */
+ u_quad_t lfs_maxfilesize; /* maximum representable file size */
+ u_long lfs_dbpseg; /* disk blocks per segment */
+ u_long lfs_inopb; /* inodes per block */
+ u_long lfs_ifpb; /* IFILE entries per block */
+ u_long lfs_sepb; /* SEGUSE entries per block */
+ u_long lfs_nindir; /* indirect pointers per block */
+ u_long lfs_nseg; /* number of segments */
+ u_long lfs_nspf; /* number of sectors per fragment */
+ u_long lfs_cleansz; /* cleaner info size in blocks */
+ u_long lfs_segtabsz; /* segment table size in blocks */
+
+ u_long lfs_segmask; /* calculate offset within a segment */
+ u_long lfs_segshift; /* fast mult/div for segments */
+ u_long lfs_bmask; /* calc block offset from file offset */
+ u_long lfs_bshift; /* calc block number from file offset */
+ u_long lfs_ffmask; /* calc frag offset from file offset */
+ u_long lfs_ffshift; /* fast mult/div for frag from file */
+ u_long lfs_fbmask; /* calc frag offset from block offset */
+ u_long lfs_fbshift; /* fast mult/div for frag from block */
+ u_long lfs_fsbtodb; /* fsbtodb and dbtofsb shift constant */
+ u_long lfs_sushift; /* fast mult/div for segusage table */
+
+#define LFS_MIN_SBINTERVAL 5 /* minimum superblock segment spacing */
+#define LFS_MAXNUMSB 10 /* superblock disk offsets */
+ daddr_t lfs_sboffs[LFS_MAXNUMSB];
+
+/* These fields are set at mount time and are meaningless on disk. */
+ struct segment *lfs_sp; /* current segment being written */
+ struct vnode *lfs_ivnode; /* vnode for the ifile */
+ u_long lfs_seglock; /* single-thread the segment writer */
+ pid_t lfs_lockpid; /* pid of lock holder */
+ u_long lfs_iocount; /* number of ios pending */
+ u_long lfs_writer; /* don't allow any dirops to start */
+ u_long lfs_dirops; /* count of active directory ops */
+ u_long lfs_doifile; /* Write ifile blocks on next write */
+ u_long lfs_nactive; /* Number of segments since last ckp */
+ u_char lfs_fmod; /* super block modified flag */
+ u_char lfs_clean; /* file system is clean flag */
+ u_char lfs_ronly; /* mounted read-only flag */
+ u_char lfs_flags; /* currently unused flag */
+ u_char lfs_fsmnt[MNAMELEN]; /* name mounted on */
+ u_char pad[3]; /* long-align */
+
+/* Checksum; valid on disk. */
+ u_long lfs_cksum; /* checksum for superblock checking */
+};
+
+/*
+ * Inode 0 is the out-of-band inode number, inode 1 is the inode number for
+ * the IFILE, the root inode is 2 and the lost+found inode is 3.
+ */
+
+/* Fixed inode numbers. */
+#define LFS_UNUSED_INUM 0 /* out of band inode number */
+#define LFS_IFILE_INUM 1 /* IFILE inode number */
+#define LOSTFOUNDINO 3 /* lost+found inode number */
+#define LFS_FIRST_INUM 4 /* first free inode number */
+
+/* Address calculations for metadata located in the inode */
+#define S_INDIR(fs) -NDADDR
+#define D_INDIR(fs) (S_INDIR(fs) - NINDIR(fs) - 1)
+#define T_INDIR(fs) (D_INDIR(fs) - NINDIR(fs) * NINDIR(fs) - 1)
+
+/* Unassigned disk address. */
+#define UNASSIGNED -1
+
+/* Unused logical block number */
+#define LFS_UNUSED_LBN -1
+
+typedef struct ifile IFILE;
+struct ifile {
+ u_long if_version; /* inode version number */
+#define LFS_UNUSED_DADDR 0 /* out-of-band daddr */
+ daddr_t if_daddr; /* inode disk address */
+ ino_t if_nextfree; /* next-unallocated inode */
+};
+
+/*
+ * Cleaner information structure. This resides in the ifile and is used
+ * to pass information between the cleaner and the kernel.
+ */
+typedef struct _cleanerinfo {
+ u_long clean; /* K: number of clean segments */
+ u_long dirty; /* K: number of dirty segments */
+} CLEANERINFO;
+
+#define CLEANSIZE_SU(fs) \
+ ((sizeof(CLEANERINFO) + (fs)->lfs_bsize - 1) >> (fs)->lfs_bshift)
+
+/*
+ * All summary blocks are the same size, so we can always read a summary
+ * block easily from a segment.
+ */
+#define LFS_SUMMARY_SIZE 512
+
+/* On-disk segment summary information */
+typedef struct segsum SEGSUM;
+struct segsum {
+ u_long ss_sumsum; /* check sum of summary block */
+ u_long ss_datasum; /* check sum of data */
+ daddr_t ss_next; /* next segment */
+ u_long ss_create; /* creation time stamp */
+ u_short ss_nfinfo; /* number of file info structures */
+ u_short ss_ninos; /* number of inodes in summary */
+#define SS_DIROP 0x01 /* segment begins a dirop */
+#define SS_CONT 0x02 /* more partials to finish this write*/
+ u_short ss_flags; /* used for directory operations */
+ u_short ss_pad; /* extra space */
+ /* FINFO's and inode daddr's... */
+};
+
+/* NINDIR is the number of indirects in a file system block. */
+#define NINDIR(fs) ((fs)->lfs_nindir)
+
+/* INOPB is the number of inodes in a secondary storage block. */
+#define INOPB(fs) ((fs)->lfs_inopb)
+
+#define blksize(fs) ((fs)->lfs_bsize)
+#define blkoff(fs, loc) ((loc) & (fs)->lfs_bmask)
+#define fsbtodb(fs, b) ((b) << (fs)->lfs_fsbtodb)
+#define dbtofsb(fs, b) ((b) >> (fs)->lfs_fsbtodb)
+#define lblkno(fs, loc) ((loc) >> (fs)->lfs_bshift)
+#define lblktosize(fs, blk) ((blk) << (fs)->lfs_bshift)
+#define numfrags(fs, loc) /* calculates (loc / fs->fs_fsize) */ \
+ ((loc) >> (fs)->lfs_bshift)
+
+#define datosn(fs, daddr) /* disk address to segment number */ \
+ (((daddr) - (fs)->lfs_sboffs[0]) / fsbtodb((fs), (fs)->lfs_ssize))
+#define sntoda(fs, sn) /* segment number to disk address */ \
+ ((daddr_t)((sn) * ((fs)->lfs_ssize << (fs)->lfs_fsbtodb) + \
+ (fs)->lfs_sboffs[0]))
+
+/* Read in the block with the cleaner info from the ifile. */
+#define LFS_CLEANERINFO(CP, F, BP) { \
+ VTOI((F)->lfs_ivnode)->i_flag |= IN_ACCESS; \
+ if (bread((F)->lfs_ivnode, \
+ (daddr_t)0, (F)->lfs_bsize, NOCRED, &(BP))) \
+ panic("lfs: ifile read"); \
+ (CP) = (CLEANERINFO *)(BP)->b_data; \
+}
+
+/* Read in the block with a specific inode from the ifile. */
+#define LFS_IENTRY(IP, F, IN, BP) { \
+ int _e; \
+ VTOI((F)->lfs_ivnode)->i_flag |= IN_ACCESS; \
+ if (_e = bread((F)->lfs_ivnode, \
+ (IN) / (F)->lfs_ifpb + (F)->lfs_cleansz + (F)->lfs_segtabsz,\
+ (F)->lfs_bsize, NOCRED, &(BP))) \
+ panic("lfs: ifile read %d", _e); \
+ (IP) = (IFILE *)(BP)->b_data + (IN) % (F)->lfs_ifpb; \
+}
+
+/* Read in the block with a specific segment usage entry from the ifile. */
+#define LFS_SEGENTRY(SP, F, IN, BP) { \
+ int _e; \
+ VTOI((F)->lfs_ivnode)->i_flag |= IN_ACCESS; \
+ if (_e = bread((F)->lfs_ivnode, \
+ ((IN) >> (F)->lfs_sushift) + (F)->lfs_cleansz, \
+ (F)->lfs_bsize, NOCRED, &(BP))) \
+ panic("lfs: ifile read: %d", _e); \
+ (SP) = (SEGUSE *)(BP)->b_data + ((IN) & (F)->lfs_sepb - 1); \
+}
+
+/*
+ * Determine if there is enough room currently available to write db
+ * disk blocks. We need enough blocks for the new blocks, the current,
+ * inode blocks, a summary block, plus potentially the ifile inode and
+ * the segment usage table, plus an ifile page.
+ */
+#define LFS_FITS(fs, db) \
+ ((long)((db + ((fs)->lfs_uinodes + INOPB((fs))) / INOPB((fs)) + \
+ fsbtodb(fs, 1) + LFS_SUMMARY_SIZE / DEV_BSIZE + \
+ (fs)->lfs_segtabsz)) < (fs)->lfs_avail)
+
+/* Determine if a buffer belongs to the ifile */
+#define IS_IFILE(bp) (VTOI(bp->b_vp)->i_number == LFS_IFILE_INUM)
+
+/*
+ * Structures used by lfs_bmapv and lfs_markv to communicate information
+ * about inodes and data blocks.
+ */
+typedef struct block_info {
+ ino_t bi_inode; /* inode # */
+ daddr_t bi_lbn; /* logical block w/in file */
+ daddr_t bi_daddr; /* disk address of block */
+ time_t bi_segcreate; /* origin segment create time */
+ int bi_version; /* file version number */
+ void *bi_bp; /* data buffer */
+} BLOCK_INFO;
+
+/* In-memory description of a segment about to be written. */
+struct segment {
+ struct lfs *fs; /* file system pointer */
+ struct buf **bpp; /* pointer to buffer array */
+ struct buf **cbpp; /* pointer to next available bp */
+ struct buf **start_bpp; /* pointer to first bp in this set */
+ struct buf *ibp; /* buffer pointer to inode page */
+ struct finfo *fip; /* current fileinfo pointer */
+ struct vnode *vp; /* vnode being gathered */
+ void *segsum; /* segment summary info */
+ u_long ninodes; /* number of inodes in this segment */
+ u_long seg_bytes_left; /* bytes left in segment */
+ u_long sum_bytes_left; /* bytes left in summary block */
+ u_long seg_number; /* number of this segment */
+ daddr_t *start_lbp; /* beginning lbn for this set */
+#define SEGM_CKP 0x01 /* doing a checkpoint */
+#define SEGM_CLEAN 0x02 /* cleaner call; don't sort */
+#define SEGM_SYNC 0x04 /* wait for segment */
+ u_long seg_flags; /* run-time flags for this segment */
+};
+
+#define ISSPACE(F, BB, C) \
+ (((C)->cr_uid == 0 && (F)->lfs_bfree >= (BB)) || \
+ ((C)->cr_uid != 0 && IS_FREESPACE(F, BB)))
+
+#define IS_FREESPACE(F, BB) \
+ ((F)->lfs_bfree > ((F)->lfs_dsize * (F)->lfs_minfree / 100 + (BB)))
+
+#define ISSPACE_XXX(F, BB) \
+ ((F)->lfs_bfree >= (BB))
+
+#define DOSTATS
+#ifdef DOSTATS
+/* Statistics Counters */
+struct lfs_stats {
+ int segsused;
+ int psegwrites;
+ int psyncwrites;
+ int pcleanwrites;
+ int blocktot;
+ int cleanblocks;
+ int ncheckpoints;
+ int nwrites;
+ int nsync_writes;
+ int wait_exceeded;
+ int write_exceeded;
+ int flush_invoked;
+};
+extern struct lfs_stats lfs_stats;
+#endif
diff --git a/sys/ufs/lfs/lfs_alloc.c b/sys/ufs/lfs/lfs_alloc.c
new file mode 100644
index 000000000000..3f06c8139304
--- /dev/null
+++ b/sys/ufs/lfs/lfs_alloc.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ *
+ * @(#)lfs_alloc.c 8.4 (Berkeley) 1/4/94
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/buf.h>
+#include <sys/vnode.h>
+#include <sys/syslog.h>
+#include <sys/mount.h>
+#include <sys/malloc.h>
+
+#include <vm/vm.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufsmount.h>
+
+#include <ufs/lfs/lfs.h>
+#include <ufs/lfs/lfs_extern.h>
+
+extern u_long nextgennumber;
+
+/* Allocate a new inode. */
+/* ARGSUSED */
+int
+lfs_valloc(ap)
+ struct vop_valloc_args /* {
+ struct vnode *a_pvp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct vnode **a_vpp;
+ } */ *ap;
+{
+ struct lfs *fs;
+ struct buf *bp;
+ struct ifile *ifp;
+ struct inode *ip;
+ struct vnode *vp;
+ daddr_t blkno;
+ ino_t new_ino;
+ u_long i, max;
+ int error;
+
+ /* Get the head of the freelist. */
+ fs = VTOI(ap->a_pvp)->i_lfs;
+ new_ino = fs->lfs_free;
+#ifdef ALLOCPRINT
+ printf("lfs_ialloc: allocate inode %d\n", new_ino);
+#endif
+
+ /*
+ * Remove the inode from the free list and write the new start
+ * of the free list into the superblock.
+ */
+ LFS_IENTRY(ifp, fs, new_ino, bp);
+ if (ifp->if_daddr != LFS_UNUSED_DADDR)
+ panic("lfs_ialloc: inuse inode on the free list");
+ fs->lfs_free = ifp->if_nextfree;
+ brelse(bp);
+
+ /* Extend IFILE so that the next lfs_valloc will succeed. */
+ if (fs->lfs_free == LFS_UNUSED_INUM) {
+ vp = fs->lfs_ivnode;
+ ip = VTOI(vp);
+ blkno = lblkno(fs, ip->i_size);
+ lfs_balloc(vp, fs->lfs_bsize, blkno, &bp);
+ ip->i_size += fs->lfs_bsize;
+ vnode_pager_setsize(vp, (u_long)ip->i_size);
+ vnode_pager_uncache(vp);
+
+ i = (blkno - fs->lfs_segtabsz - fs->lfs_cleansz) *
+ fs->lfs_ifpb;
+ fs->lfs_free = i;
+ max = i + fs->lfs_ifpb;
+ for (ifp = (struct ifile *)bp->b_data; i < max; ++ifp) {
+ ifp->if_version = 1;
+ ifp->if_daddr = LFS_UNUSED_DADDR;
+ ifp->if_nextfree = ++i;
+ }
+ ifp--;
+ ifp->if_nextfree = LFS_UNUSED_INUM;
+ if (error = VOP_BWRITE(bp))
+ return (error);
+ }
+
+ /* Create a vnode to associate with the inode. */
+ if (error = lfs_vcreate(ap->a_pvp->v_mount, new_ino, &vp))
+ return (error);
+
+
+ ip = VTOI(vp);
+ /* Zero out the direct and indirect block addresses. */
+ bzero(&ip->i_din, sizeof(struct dinode));
+ ip->i_din.di_inumber = new_ino;
+
+ /* Set a new generation number for this inode. */
+ if (++nextgennumber < (u_long)time.tv_sec)
+ nextgennumber = time.tv_sec;
+ ip->i_gen = nextgennumber;
+
+ /* Insert into the inode hash table. */
+ ufs_ihashins(ip);
+
+ if (error = ufs_vinit(vp->v_mount, lfs_specop_p, LFS_FIFOOPS, &vp)) {
+ vput(vp);
+ *ap->a_vpp = NULL;
+ return (error);
+ }
+
+ *ap->a_vpp = vp;
+ vp->v_flag |= VDIROP;
+ VREF(ip->i_devvp);
+
+ /* Set superblock modified bit and increment file count. */
+ fs->lfs_fmod = 1;
+ ++fs->lfs_nfiles;
+ return (0);
+}
+
+/* Create a new vnode/inode pair and initialize what fields we can. */
+int
+lfs_vcreate(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+ extern int (**lfs_vnodeop_p)();
+ struct inode *ip;
+ struct ufsmount *ump;
+ int error, i;
+
+ /* Create the vnode. */
+ if (error = getnewvnode(VT_LFS, mp, lfs_vnodeop_p, vpp)) {
+ *vpp = NULL;
+ return (error);
+ }
+
+ /* Get a pointer to the private mount structure. */
+ ump = VFSTOUFS(mp);
+
+ /* Initialize the inode. */
+ MALLOC(ip, struct inode *, sizeof(struct inode), M_LFSNODE, M_WAITOK);
+ (*vpp)->v_data = ip;
+ ip->i_vnode = *vpp;
+ ip->i_devvp = ump->um_devvp;
+ ip->i_flag = IN_MODIFIED;
+ ip->i_dev = ump->um_dev;
+ ip->i_number = ip->i_din.di_inumber = ino;
+ip->i_din.di_spare[0] = 0xdeadbeef;
+ip->i_din.di_spare[1] = 0xdeadbeef;
+ ip->i_lfs = ump->um_lfs;
+#ifdef QUOTA
+ for (i = 0; i < MAXQUOTAS; i++)
+ ip->i_dquot[i] = NODQUOT;
+#endif
+ ip->i_lockf = 0;
+ ip->i_diroff = 0;
+ ip->i_mode = 0;
+ ip->i_size = 0;
+ ip->i_blocks = 0;
+ ++ump->um_lfs->lfs_uinodes;
+ return (0);
+}
+
+/* Free an inode. */
+/* ARGUSED */
+int
+lfs_vfree(ap)
+ struct vop_vfree_args /* {
+ struct vnode *a_pvp;
+ ino_t a_ino;
+ int a_mode;
+ } */ *ap;
+{
+ SEGUSE *sup;
+ struct buf *bp;
+ struct ifile *ifp;
+ struct inode *ip;
+ struct lfs *fs;
+ daddr_t old_iaddr;
+ ino_t ino;
+
+ /* Get the inode number and file system. */
+ ip = VTOI(ap->a_pvp);
+ fs = ip->i_lfs;
+ ino = ip->i_number;
+ if (ip->i_flag & IN_MODIFIED) {
+ --fs->lfs_uinodes;
+ ip->i_flag &=
+ ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
+ }
+ /*
+ * Set the ifile's inode entry to unused, increment its version number
+ * and link it into the free chain.
+ */
+ LFS_IENTRY(ifp, fs, ino, bp);
+ old_iaddr = ifp->if_daddr;
+ ifp->if_daddr = LFS_UNUSED_DADDR;
+ ++ifp->if_version;
+ ifp->if_nextfree = fs->lfs_free;
+ fs->lfs_free = ino;
+ (void) VOP_BWRITE(bp);
+
+ if (old_iaddr != LFS_UNUSED_DADDR) {
+ LFS_SEGENTRY(sup, fs, datosn(fs, old_iaddr), bp);
+#ifdef DIAGNOSTIC
+ if (sup->su_nbytes < sizeof(struct dinode))
+ panic("lfs_vfree: negative byte count (segment %d)\n",
+ datosn(fs, old_iaddr));
+#endif
+ sup->su_nbytes -= sizeof(struct dinode);
+ (void) VOP_BWRITE(bp);
+ }
+
+ /* Set superblock modified bit and decrement file count. */
+ fs->lfs_fmod = 1;
+ --fs->lfs_nfiles;
+ return (0);
+}
diff --git a/sys/ufs/lfs/lfs_balloc.c b/sys/ufs/lfs/lfs_balloc.c
new file mode 100644
index 000000000000..b56bc9ec51b2
--- /dev/null
+++ b/sys/ufs/lfs/lfs_balloc.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ *
+ * @(#)lfs_balloc.c 8.1 (Berkeley) 6/11/93
+ */
+#include <sys/param.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/resourcevar.h>
+#include <sys/trace.h>
+
+#include <miscfs/specfs/specdev.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufsmount.h>
+
+#include <ufs/lfs/lfs.h>
+#include <ufs/lfs/lfs_extern.h>
+
+int
+lfs_balloc(vp, iosize, lbn, bpp)
+ struct vnode *vp;
+ u_long iosize;
+ daddr_t lbn;
+ struct buf **bpp;
+{
+ struct buf *ibp, *bp;
+ struct inode *ip;
+ struct lfs *fs;
+ struct indir indirs[NIADDR+2];
+ daddr_t daddr;
+ int bb, error, i, num;
+
+ ip = VTOI(vp);
+ fs = ip->i_lfs;
+
+ /*
+ * Three cases: it's a block beyond the end of file, it's a block in
+ * the file that may or may not have been assigned a disk address or
+ * we're writing an entire block. Note, if the daddr is unassigned,
+ * the block might still have existed in the cache (if it was read
+ * or written earlier). If it did, make sure we don't count it as a
+ * new block or zero out its contents. If it did not, make sure
+ * we allocate any necessary indirect blocks.
+ */
+
+ *bpp = NULL;
+ if (error = ufs_bmaparray(vp, lbn, &daddr, &indirs[0], &num, NULL ))
+ return (error);
+
+ *bpp = bp = getblk(vp, lbn, fs->lfs_bsize, 0, 0);
+ bb = VFSTOUFS(vp->v_mount)->um_seqinc;
+ if (daddr == UNASSIGNED)
+ /* May need to allocate indirect blocks */
+ for (i = 1; i < num; ++i)
+ if (!indirs[i].in_exists) {
+ ibp =
+ getblk(vp, indirs[i].in_lbn, fs->lfs_bsize,
+ 0, 0);
+ if (!(ibp->b_flags & (B_DONE | B_DELWRI))) {
+ if (!ISSPACE(fs, bb, curproc->p_ucred)){
+ ibp->b_flags |= B_INVAL;
+ brelse(ibp);
+ error = ENOSPC;
+ } else {
+ ip->i_blocks += bb;
+ ip->i_lfs->lfs_bfree -= bb;
+ clrbuf(ibp);
+ error = VOP_BWRITE(ibp);
+ }
+ } else
+ panic ("Indirect block should not exist");
+ }
+ if (error) {
+ if (bp)
+ brelse(bp);
+ return(error);
+ }
+
+
+ /* Now, we may need to allocate the data block */
+ if (!(bp->b_flags & (B_CACHE | B_DONE | B_DELWRI))) {
+ if (daddr == UNASSIGNED)
+ if (!ISSPACE(fs, bb, curproc->p_ucred)) {
+ bp->b_flags |= B_INVAL;
+ brelse(bp);
+ return(ENOSPC);
+ } else {
+ ip->i_blocks += bb;
+ ip->i_lfs->lfs_bfree -= bb;
+ if (iosize != fs->lfs_bsize)
+ clrbuf(bp);
+ }
+ else if (iosize == fs->lfs_bsize)
+ bp->b_blkno = daddr; /* Skip the I/O */
+ else {
+ bp->b_blkno = daddr;
+ bp->b_flags |= B_READ;
+ VOP_STRATEGY(bp);
+ return(biowait(bp));
+ }
+ }
+ return (error);
+}
diff --git a/sys/ufs/lfs/lfs_bio.c b/sys/ufs/lfs/lfs_bio.c
new file mode 100644
index 000000000000..0f021f172088
--- /dev/null
+++ b/sys/ufs/lfs/lfs_bio.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ *
+ * @(#)lfs_bio.c 8.4 (Berkeley) 12/30/93
+ */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/buf.h>
+#include <sys/vnode.h>
+#include <sys/resourcevar.h>
+#include <sys/mount.h>
+#include <sys/kernel.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufsmount.h>
+
+#include <ufs/lfs/lfs.h>
+#include <ufs/lfs/lfs_extern.h>
+
+/*
+ * LFS block write function.
+ *
+ * XXX
+ * No write cost accounting is done.
+ * This is almost certainly wrong for synchronous operations and NFS.
+ */
+int lfs_allclean_wakeup; /* Cleaner wakeup address. */
+int locked_queue_count; /* XXX Count of locked-down buffers. */
+int lfs_writing; /* Set if already kicked off a writer
+ because of buffer space */
+/*
+#define WRITE_THRESHHOLD ((nbuf >> 2) - 10)
+#define WAIT_THRESHHOLD ((nbuf >> 1) - 10)
+*/
+#define WAIT_THRESHHOLD (nbuf - (nbuf >> 2) - 10)
+#define WRITE_THRESHHOLD ((nbuf >> 1) - 10)
+#define LFS_BUFWAIT 2
+
+int
+lfs_bwrite(ap)
+ struct vop_bwrite_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+ register struct buf *bp = ap->a_bp;
+ struct lfs *fs;
+ struct inode *ip;
+ int error, s;
+
+ /*
+ * Set the delayed write flag and use reassignbuf to move the buffer
+ * from the clean list to the dirty one.
+ *
+ * Set the B_LOCKED flag and unlock the buffer, causing brelse to move
+ * the buffer onto the LOCKED free list. This is necessary, otherwise
+ * getnewbuf() would try to reclaim the buffers using bawrite, which
+ * isn't going to work.
+ *
+ * XXX we don't let meta-data writes run out of space because they can
+ * come from the segment writer. We need to make sure that there is
+ * enough space reserved so that there's room to write meta-data
+ * blocks.
+ */
+ if (!(bp->b_flags & B_LOCKED)) {
+ fs = VFSTOUFS(bp->b_vp->v_mount)->um_lfs;
+ while (!LFS_FITS(fs, fsbtodb(fs, 1)) && !IS_IFILE(bp) &&
+ bp->b_lblkno > 0) {
+ /* Out of space, need cleaner to run */
+ wakeup(&lfs_allclean_wakeup);
+ if (error = tsleep(&fs->lfs_avail, PCATCH | PUSER,
+ "cleaner", NULL)) {
+ brelse(bp);
+ return (error);
+ }
+ }
+ ip = VTOI((bp)->b_vp);
+ if (!(ip->i_flag & IN_MODIFIED))
+ ++fs->lfs_uinodes;
+ ip->i_flag |= IN_CHANGE | IN_MODIFIED | IN_UPDATE;
+ fs->lfs_avail -= fsbtodb(fs, 1);
+ ++locked_queue_count;
+ bp->b_flags |= B_DELWRI | B_LOCKED;
+ bp->b_flags &= ~(B_READ | B_ERROR);
+ s = splbio();
+ reassignbuf(bp, bp->b_vp);
+ splx(s);
+ }
+ brelse(bp);
+ return (0);
+}
+
+/*
+ * XXX
+ * This routine flushes buffers out of the B_LOCKED queue when LFS has too
+ * many locked down. Eventually the pageout daemon will simply call LFS
+ * when pages need to be reclaimed. Note, we have one static count of locked
+ * buffers, so we can't have more than a single file system. To make this
+ * work for multiple file systems, put the count into the mount structure.
+ */
+void
+lfs_flush()
+{
+ register struct mount *mp;
+
+#ifdef DOSTATS
+ ++lfs_stats.write_exceeded;
+#endif
+ if (lfs_writing)
+ return;
+ lfs_writing = 1;
+ for (mp = mountlist.tqh_first; mp != NULL; mp = mp->mnt_list.tqe_next) {
+ /* The lock check below is to avoid races with unmount. */
+ if (mp->mnt_stat.f_type == MOUNT_LFS &&
+ (mp->mnt_flag & (MNT_MLOCK|MNT_RDONLY|MNT_UNMOUNT)) == 0 &&
+ !((((struct ufsmount *)mp->mnt_data))->ufsmount_u.lfs)->lfs_dirops ) {
+ /*
+ * We set the queue to 0 here because we are about to
+ * write all the dirty buffers we have. If more come
+ * in while we're writing the segment, they may not
+ * get written, so we want the count to reflect these
+ * new writes after the segwrite completes.
+ */
+#ifdef DOSTATS
+ ++lfs_stats.flush_invoked;
+#endif
+ lfs_segwrite(mp, 0);
+ }
+ }
+ lfs_writing = 0;
+}
+
+int
+lfs_check(vp, blkno)
+ struct vnode *vp;
+ daddr_t blkno;
+{
+ extern int lfs_allclean_wakeup;
+ int error;
+
+ error = 0;
+ if (incore(vp, blkno))
+ return (0);
+ if (locked_queue_count > WRITE_THRESHHOLD)
+ lfs_flush();
+
+ /* If out of buffers, wait on writer */
+ while (locked_queue_count > WAIT_THRESHHOLD) {
+#ifdef DOSTATS
+ ++lfs_stats.wait_exceeded;
+#endif
+ error = tsleep(&locked_queue_count, PCATCH | PUSER, "buffers",
+ hz * LFS_BUFWAIT);
+ }
+
+ return (error);
+}
diff --git a/sys/ufs/lfs/lfs_cksum.c b/sys/ufs/lfs/lfs_cksum.c
new file mode 100644
index 000000000000..77b011aa2c48
--- /dev/null
+++ b/sys/ufs/lfs/lfs_cksum.c
@@ -0,0 +1,58 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ *
+ * @(#)lfs_cksum.c 8.1 (Berkeley) 6/11/93
+ */
+
+#include <sys/types.h>
+
+/*
+ * Simple, general purpose, fast checksum. Data must be short-aligned.
+ * Returns a u_long in case we ever want to do something more rigorous.
+ *
+ * XXX
+ * Use the TCP/IP checksum instead.
+ */
+u_long
+cksum(str, len)
+ register void *str;
+ register size_t len;
+{
+ register u_long sum;
+
+ len &= ~(sizeof(u_short) - 1);
+ for (sum = 0; len; len -= sizeof(u_short)) {
+ sum ^= *(u_short *)str;
+ ++(u_short *)str;
+ }
+ return (sum);
+}
diff --git a/sys/ufs/lfs/lfs_debug.c b/sys/ufs/lfs/lfs_debug.c
new file mode 100644
index 000000000000..cc28d6090239
--- /dev/null
+++ b/sys/ufs/lfs/lfs_debug.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ *
+ * @(#)lfs_debug.c 8.1 (Berkeley) 6/11/93
+ */
+
+#ifdef DEBUG
+#include <sys/param.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/lfs/lfs.h>
+#include <ufs/lfs/lfs_extern.h>
+
+void
+lfs_dump_super(lfsp)
+ struct lfs *lfsp;
+{
+ int i;
+
+ (void)printf("%s%lx\t%s%lx\t%s%d\t%s%d\n",
+ "magic ", lfsp->lfs_magic,
+ "version ", lfsp->lfs_version,
+ "size ", lfsp->lfs_size,
+ "ssize ", lfsp->lfs_ssize);
+ (void)printf("%s%d\t%s%d\t%s%d\t%s%d\n",
+ "dsize ", lfsp->lfs_dsize,
+ "bsize ", lfsp->lfs_bsize,
+ "fsize ", lfsp->lfs_fsize,
+ "frag ", lfsp->lfs_frag);
+
+ (void)printf("%s%d\t%s%d\t%s%d\t%s%d\n",
+ "minfree ", lfsp->lfs_minfree,
+ "inopb ", lfsp->lfs_inopb,
+ "ifpb ", lfsp->lfs_ifpb,
+ "nindir ", lfsp->lfs_nindir);
+
+ (void)printf("%s%d\t%s%d\t%s%d\t%s%d\n",
+ "nseg ", lfsp->lfs_nseg,
+ "nspf ", lfsp->lfs_nspf,
+ "cleansz ", lfsp->lfs_cleansz,
+ "segtabsz ", lfsp->lfs_segtabsz);
+
+ (void)printf("%s%lx\t%s%d\t%s%lx\t%s%d\n",
+ "segmask ", lfsp->lfs_segmask,
+ "segshift ", lfsp->lfs_segshift,
+ "bmask ", lfsp->lfs_bmask,
+ "bshift ", lfsp->lfs_bshift);
+
+ (void)printf("%s%lx\t%s%d\t%s%lx\t%s%d\n",
+ "ffmask ", lfsp->lfs_ffmask,
+ "ffshift ", lfsp->lfs_ffshift,
+ "fbmask ", lfsp->lfs_fbmask,
+ "fbshift ", lfsp->lfs_fbshift);
+
+ (void)printf("%s%d\t%s%d\t%s%lx\t%s%qx\n",
+ "sushift ", lfsp->lfs_sushift,
+ "fsbtodb ", lfsp->lfs_fsbtodb,
+ "cksum ", lfsp->lfs_cksum,
+ "maxfilesize ", lfsp->lfs_maxfilesize);
+
+ (void)printf("Superblock disk addresses:");
+ for (i = 0; i < LFS_MAXNUMSB; i++)
+ (void)printf(" %lx", lfsp->lfs_sboffs[i]);
+ (void)printf("\n");
+
+ (void)printf("Checkpoint Info\n");
+ (void)printf("%s%d\t%s%lx\t%s%d\n",
+ "free ", lfsp->lfs_free,
+ "idaddr ", lfsp->lfs_idaddr,
+ "ifile ", lfsp->lfs_ifile);
+ (void)printf("%s%lx\t%s%d\t%s%lx\t%s%lx\t%s%lx\t%s%lx\n",
+ "bfree ", lfsp->lfs_bfree,
+ "nfiles ", lfsp->lfs_nfiles,
+ "lastseg ", lfsp->lfs_lastseg,
+ "nextseg ", lfsp->lfs_nextseg,
+ "curseg ", lfsp->lfs_curseg,
+ "offset ", lfsp->lfs_offset);
+ (void)printf("tstamp %lx\n", lfsp->lfs_tstamp);
+}
+
+void
+lfs_dump_dinode(dip)
+ struct dinode *dip;
+{
+ int i;
+
+ (void)printf("%s%u\t%s%d\t%s%u\t%s%u\t%s%lu\n",
+ "mode ", dip->di_mode,
+ "nlink ", dip->di_nlink,
+ "uid ", dip->di_uid,
+ "gid ", dip->di_gid,
+ "size ", dip->di_size);
+ (void)printf("inum %ld\n", dip->di_inumber);
+ (void)printf("Direct Addresses\n");
+ for (i = 0; i < NDADDR; i++) {
+ (void)printf("\t%lx", dip->di_db[i]);
+ if ((i % 6) == 5)
+ (void)printf("\n");
+ }
+ for (i = 0; i < NIADDR; i++)
+ (void)printf("\t%lx", dip->di_ib[i]);
+ (void)printf("\n");
+}
+#endif /* DEBUG */
diff --git a/sys/ufs/lfs/lfs_extern.h b/sys/ufs/lfs/lfs_extern.h
new file mode 100644
index 000000000000..c1157ade02af
--- /dev/null
+++ b/sys/ufs/lfs/lfs_extern.h
@@ -0,0 +1,106 @@
+/*-
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ *
+ * @(#)lfs_extern.h 8.2 (Berkeley) 4/16/94
+ */
+
+struct fid;
+struct mount;
+struct nameidata;
+struct proc;
+struct statfs;
+struct timeval;
+struct inode;
+struct uio;
+struct mbuf;
+
+__BEGIN_DECLS
+u_long cksum __P((void *, size_t)); /* XXX */
+int lfs_balloc __P((struct vnode *, u_long, daddr_t, struct buf **));
+int lfs_blkatoff __P((struct vop_blkatoff_args *));
+int lfs_bwrite __P((struct vop_bwrite_args *));
+int lfs_check __P((struct vnode *, daddr_t));
+int lfs_close __P((struct vop_close_args *));
+int lfs_create __P((struct vop_create_args *));
+int lfs_fhtovp __P((struct mount *, struct fid *, struct mbuf *,
+ struct vnode **, int *, struct ucred **));
+int lfs_fsync __P((struct vop_fsync_args *));
+int lfs_getattr __P((struct vop_getattr_args *));
+struct dinode *
+ lfs_ifind __P((struct lfs *, ino_t, struct dinode *));
+int lfs_inactive __P((struct vop_inactive_args *));
+int lfs_init __P((void));
+int lfs_initseg __P((struct lfs *));
+int lfs_link __P((struct vop_link_args *));
+int lfs_makeinode __P((int, struct nameidata *, struct inode **));
+int lfs_mkdir __P((struct vop_mkdir_args *));
+int lfs_mknod __P((struct vop_mknod_args *));
+int lfs_mount __P((struct mount *,
+ char *, caddr_t, struct nameidata *, struct proc *));
+int lfs_mountroot __P((void));
+struct buf *
+ lfs_newbuf __P((struct vnode *, daddr_t, size_t));
+int lfs_read __P((struct vop_read_args *));
+int lfs_remove __P((struct vop_remove_args *));
+int lfs_rmdir __P((struct vop_rmdir_args *));
+int lfs_rename __P((struct vop_rename_args *));
+void lfs_seglock __P((struct lfs *, unsigned long flags));
+void lfs_segunlock __P((struct lfs *));
+int lfs_segwrite __P((struct mount *, int));
+int lfs_statfs __P((struct mount *, struct statfs *, struct proc *));
+int lfs_symlink __P((struct vop_symlink_args *));
+int lfs_sync __P((struct mount *, int, struct ucred *, struct proc *));
+int lfs_truncate __P((struct vop_truncate_args *));
+int lfs_unmount __P((struct mount *, int, struct proc *));
+int lfs_update __P((struct vop_update_args *));
+int lfs_valloc __P((struct vop_valloc_args *));
+int lfs_vcreate __P((struct mount *, ino_t, struct vnode **));
+int lfs_vfree __P((struct vop_vfree_args *));
+int lfs_vflush __P((struct vnode *));
+int lfs_vget __P((struct mount *, ino_t, struct vnode **));
+int lfs_vptofh __P((struct vnode *, struct fid *));
+int lfs_vref __P((struct vnode *));
+void lfs_vunref __P((struct vnode *));
+int lfs_write __P((struct vop_write_args *));
+#ifdef DEBUG
+void lfs_dump_dinode __P((struct dinode *));
+void lfs_dump_super __P((struct lfs *));
+#endif
+__END_DECLS
+extern int (**lfs_vnodeop_p)();
+extern int (**lfs_specop_p)();
+#ifdef FIFO
+extern int (**lfs_fifoop_p)();
+#define LFS_FIFOOPS lfs_fifoop_p
+#else
+#define LFS_FIFOOPS NULL
+#endif
diff --git a/sys/ufs/lfs/lfs_inode.c b/sys/ufs/lfs/lfs_inode.c
new file mode 100644
index 000000000000..1a06aa23ed8f
--- /dev/null
+++ b/sys/ufs/lfs/lfs_inode.c
@@ -0,0 +1,359 @@
+/*
+ * Copyright (c) 1986, 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ *
+ * @(#)lfs_inode.c 8.5 (Berkeley) 12/30/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mount.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <sys/buf.h>
+#include <sys/vnode.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+#include <vm/vm.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/ufs_extern.h>
+
+#include <ufs/lfs/lfs.h>
+#include <ufs/lfs/lfs_extern.h>
+
+int
+lfs_init()
+{
+ return (ufs_init());
+}
+
+/* Search a block for a specific dinode. */
+struct dinode *
+lfs_ifind(fs, ino, dip)
+ struct lfs *fs;
+ ino_t ino;
+ register struct dinode *dip;
+{
+ register int cnt;
+ register struct dinode *ldip;
+
+ for (cnt = INOPB(fs), ldip = dip + (cnt - 1); cnt--; --ldip)
+ if (ldip->di_inumber == ino)
+ return (ldip);
+
+ panic("lfs_ifind: dinode %u not found", ino);
+ /* NOTREACHED */
+}
+
+int
+lfs_update(ap)
+ struct vop_update_args /* {
+ struct vnode *a_vp;
+ struct timeval *a_access;
+ struct timeval *a_modify;
+ int a_waitfor;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct inode *ip;
+
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ return (0);
+ ip = VTOI(vp);
+ if ((ip->i_flag &
+ (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0)
+ return (0);
+ if (ip->i_flag & IN_ACCESS)
+ ip->i_atime.ts_sec = ap->a_access->tv_sec;
+ if (ip->i_flag & IN_UPDATE) {
+ ip->i_mtime.ts_sec = ap->a_modify->tv_sec;
+ (ip)->i_modrev++;
+ }
+ if (ip->i_flag & IN_CHANGE)
+ ip->i_ctime.ts_sec = time.tv_sec;
+ ip->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE);
+
+ if (!(ip->i_flag & IN_MODIFIED))
+ ++(VFSTOUFS(vp->v_mount)->um_lfs->lfs_uinodes);
+ ip->i_flag |= IN_MODIFIED;
+
+ /* If sync, push back the vnode and any dirty blocks it may have. */
+ return (ap->a_waitfor & LFS_SYNC ? lfs_vflush(vp) : 0);
+}
+
+/* Update segment usage information when removing a block. */
+#define UPDATE_SEGUSE \
+ if (lastseg != -1) { \
+ LFS_SEGENTRY(sup, fs, lastseg, sup_bp); \
+ if ((num << fs->lfs_bshift) > sup->su_nbytes) \
+ panic("lfs_truncate: negative bytes in segment %d\n", \
+ lastseg); \
+ sup->su_nbytes -= num << fs->lfs_bshift; \
+ e1 = VOP_BWRITE(sup_bp); \
+ blocksreleased += num; \
+ }
+
+#define SEGDEC { \
+ if (daddr != 0) { \
+ if (lastseg != (seg = datosn(fs, daddr))) { \
+ UPDATE_SEGUSE; \
+ num = 1; \
+ lastseg = seg; \
+ } else \
+ ++num; \
+ } \
+}
+
+/*
+ * Truncate the inode ip to at most length size. Update segment usage
+ * table information.
+ */
+/* ARGSUSED */
+int
+lfs_truncate(ap)
+ struct vop_truncate_args /* {
+ struct vnode *a_vp;
+ off_t a_length;
+ int a_flags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct indir *inp;
+ register int i;
+ register daddr_t *daddrp;
+ register struct vnode *vp = ap->a_vp;
+ off_t length = ap->a_length;
+ struct buf *bp, *sup_bp;
+ struct timeval tv;
+ struct ifile *ifp;
+ struct inode *ip;
+ struct lfs *fs;
+ struct indir a[NIADDR + 2], a_end[NIADDR + 2];
+ SEGUSE *sup;
+ daddr_t daddr, lastblock, lbn, olastblock;
+ long off, a_released, blocksreleased, i_released;
+ int e1, e2, depth, lastseg, num, offset, seg, size;
+
+ ip = VTOI(vp);
+ tv = time;
+ if (vp->v_type == VLNK && vp->v_mount->mnt_maxsymlinklen > 0) {
+#ifdef DIAGNOSTIC
+ if (length != 0)
+ panic("lfs_truncate: partial truncate of symlink");
+#endif
+ bzero((char *)&ip->i_shortlink, (u_int)ip->i_size);
+ ip->i_size = 0;
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ return (VOP_UPDATE(vp, &tv, &tv, 0));
+ }
+ vnode_pager_setsize(vp, (u_long)length);
+
+ fs = ip->i_lfs;
+
+ /* If length is larger than the file, just update the times. */
+ if (ip->i_size <= length) {
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ return (VOP_UPDATE(vp, &tv, &tv, 0));
+ }
+
+ /*
+ * Calculate index into inode's block list of last direct and indirect
+ * blocks (if any) which we want to keep. Lastblock is 0 when the
+ * file is truncated to 0.
+ */
+ lastblock = lblkno(fs, length + fs->lfs_bsize - 1);
+ olastblock = lblkno(fs, ip->i_size + fs->lfs_bsize - 1) - 1;
+
+ /*
+ * Update the size of the file. If the file is not being truncated to
+ * a block boundry, the contents of the partial block following the end
+ * of the file must be zero'ed in case it ever become accessable again
+ * because of subsequent file growth.
+ */
+ offset = blkoff(fs, length);
+ if (offset == 0)
+ ip->i_size = length;
+ else {
+ lbn = lblkno(fs, length);
+#ifdef QUOTA
+ if (e1 = getinoquota(ip))
+ return (e1);
+#endif
+ if (e1 = bread(vp, lbn, fs->lfs_bsize, NOCRED, &bp))
+ return (e1);
+ ip->i_size = length;
+ size = blksize(fs);
+ (void)vnode_pager_uncache(vp);
+ bzero((char *)bp->b_data + offset, (u_int)(size - offset));
+ allocbuf(bp, size);
+ if (e1 = VOP_BWRITE(bp))
+ return (e1);
+ }
+ /*
+ * Modify sup->su_nbyte counters for each deleted block; keep track
+ * of number of blocks removed for ip->i_blocks.
+ */
+ blocksreleased = 0;
+ num = 0;
+ lastseg = -1;
+
+ for (lbn = olastblock; lbn >= lastblock;) {
+ /* XXX use run length from bmap array to make this faster */
+ ufs_bmaparray(vp, lbn, &daddr, a, &depth, NULL);
+ if (lbn == olastblock)
+ for (i = NIADDR + 2; i--;)
+ a_end[i] = a[i];
+ switch (depth) {
+ case 0: /* Direct block. */
+ daddr = ip->i_db[lbn];
+ SEGDEC;
+ ip->i_db[lbn] = 0;
+ --lbn;
+ break;
+#ifdef DIAGNOSTIC
+ case 1: /* An indirect block. */
+ panic("lfs_truncate: ufs_bmaparray returned depth 1");
+ /* NOTREACHED */
+#endif
+ default: /* Chain of indirect blocks. */
+ inp = a + --depth;
+ if (inp->in_off > 0 && lbn != lastblock) {
+ lbn -= inp->in_off < lbn - lastblock ?
+ inp->in_off : lbn - lastblock;
+ break;
+ }
+ for (; depth && (inp->in_off == 0 || lbn == lastblock);
+ --inp, --depth) {
+ if (bread(vp,
+ inp->in_lbn, fs->lfs_bsize, NOCRED, &bp))
+ panic("lfs_truncate: bread bno %d",
+ inp->in_lbn);
+ daddrp = (daddr_t *)bp->b_data + inp->in_off;
+ for (i = inp->in_off;
+ i++ <= a_end[depth].in_off;) {
+ daddr = *daddrp++;
+ SEGDEC;
+ }
+ a_end[depth].in_off = NINDIR(fs) - 1;
+ if (inp->in_off == 0)
+ brelse (bp);
+ else {
+ bzero((daddr_t *)bp->b_data +
+ inp->in_off, fs->lfs_bsize -
+ inp->in_off * sizeof(daddr_t));
+ if (e1 = VOP_BWRITE(bp))
+ return (e1);
+ }
+ }
+ if (depth == 0 && a[1].in_off == 0) {
+ off = a[0].in_off;
+ daddr = ip->i_ib[off];
+ SEGDEC;
+ ip->i_ib[off] = 0;
+ }
+ if (lbn == lastblock || lbn <= NDADDR)
+ --lbn;
+ else {
+ lbn -= NINDIR(fs);
+ if (lbn < lastblock)
+ lbn = lastblock;
+ }
+ }
+ }
+ UPDATE_SEGUSE;
+
+ /* If truncating the file to 0, update the version number. */
+ if (length == 0) {
+ LFS_IENTRY(ifp, fs, ip->i_number, bp);
+ ++ifp->if_version;
+ (void) VOP_BWRITE(bp);
+ }
+
+#ifdef DIAGNOSTIC
+ if (ip->i_blocks < fsbtodb(fs, blocksreleased)) {
+ printf("lfs_truncate: block count < 0\n");
+ blocksreleased = ip->i_blocks;
+ }
+#endif
+ ip->i_blocks -= fsbtodb(fs, blocksreleased);
+ fs->lfs_bfree += fsbtodb(fs, blocksreleased);
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ /*
+ * Traverse dirty block list counting number of dirty buffers
+ * that are being deleted out of the cache, so that the lfs_avail
+ * field can be updated.
+ */
+ a_released = 0;
+ i_released = 0;
+ for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = bp->b_vnbufs.le_next)
+ if (bp->b_flags & B_LOCKED) {
+ ++a_released;
+ /*
+ * XXX
+ * When buffers are created in the cache, their block
+ * number is set equal to their logical block number.
+ * If that is still true, we are assuming that the
+ * blocks are new (not yet on disk) and weren't
+ * counted above. However, there is a slight chance
+ * that a block's disk address is equal to its logical
+ * block number in which case, we'll get an overcounting
+ * here.
+ */
+ if (bp->b_blkno == bp->b_lblkno)
+ ++i_released;
+ }
+ blocksreleased = fsbtodb(fs, i_released);
+#ifdef DIAGNOSTIC
+ if (blocksreleased > ip->i_blocks) {
+ printf("lfs_inode: Warning! %s\n",
+ "more blocks released from inode than are in inode");
+ blocksreleased = ip->i_blocks;
+ }
+#endif
+ fs->lfs_bfree += blocksreleased;
+ ip->i_blocks -= blocksreleased;
+#ifdef DIAGNOSTIC
+ if (length == 0 && ip->i_blocks != 0)
+ printf("lfs_inode: Warning! %s%d%s\n",
+ "Truncation to zero, but ", ip->i_blocks,
+ " blocks left on inode");
+#endif
+ fs->lfs_avail += fsbtodb(fs, a_released);
+ e1 = vinvalbuf(vp, (length > 0) ? V_SAVE : 0, ap->a_cred, ap->a_p,
+ 0, 0);
+ e2 = VOP_UPDATE(vp, &tv, &tv, 0);
+ return (e1 ? e1 : e2 ? e2 : 0);
+}
diff --git a/sys/ufs/lfs/lfs_segment.c b/sys/ufs/lfs/lfs_segment.c
new file mode 100644
index 000000000000..249d59ddda50
--- /dev/null
+++ b/sys/ufs/lfs/lfs_segment.c
@@ -0,0 +1,1111 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ *
+ * @(#)lfs_segment.c 8.5 (Berkeley) 1/4/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/kernel.h>
+#include <sys/resourcevar.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/conf.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+
+#include <miscfs/specfs/specdev.h>
+#include <miscfs/fifofs/fifo.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/ufs_extern.h>
+
+#include <ufs/lfs/lfs.h>
+#include <ufs/lfs/lfs_extern.h>
+
+extern int count_lock_queue __P((void));
+
+#define MAX_ACTIVE 10
+/*
+ * Determine if it's OK to start a partial in this segment, or if we need
+ * to go on to a new segment.
+ */
+#define LFS_PARTIAL_FITS(fs) \
+ ((fs)->lfs_dbpseg - ((fs)->lfs_offset - (fs)->lfs_curseg) > \
+ 1 << (fs)->lfs_fsbtodb)
+
+void lfs_callback __P((struct buf *));
+void lfs_gather __P((struct lfs *, struct segment *,
+ struct vnode *, int (*) __P((struct lfs *, struct buf *))));
+int lfs_gatherblock __P((struct segment *, struct buf *, int *));
+void lfs_iset __P((struct inode *, daddr_t, time_t));
+int lfs_match_data __P((struct lfs *, struct buf *));
+int lfs_match_dindir __P((struct lfs *, struct buf *));
+int lfs_match_indir __P((struct lfs *, struct buf *));
+int lfs_match_tindir __P((struct lfs *, struct buf *));
+void lfs_newseg __P((struct lfs *));
+void lfs_shellsort __P((struct buf **, daddr_t *, register int));
+void lfs_supercallback __P((struct buf *));
+void lfs_updatemeta __P((struct segment *));
+int lfs_vref __P((struct vnode *));
+void lfs_vunref __P((struct vnode *));
+void lfs_writefile __P((struct lfs *, struct segment *, struct vnode *));
+int lfs_writeinode __P((struct lfs *, struct segment *, struct inode *));
+int lfs_writeseg __P((struct lfs *, struct segment *));
+void lfs_writesuper __P((struct lfs *));
+void lfs_writevnodes __P((struct lfs *fs, struct mount *mp,
+ struct segment *sp, int dirops));
+
+int lfs_allclean_wakeup; /* Cleaner wakeup address. */
+
+/* Statistics Counters */
+#define DOSTATS
+struct lfs_stats lfs_stats;
+
+/* op values to lfs_writevnodes */
+#define VN_REG 0
+#define VN_DIROP 1
+#define VN_EMPTY 2
+
+/*
+ * Ifile and meta data blocks are not marked busy, so segment writes MUST be
+ * single threaded. Currently, there are two paths into lfs_segwrite, sync()
+ * and getnewbuf(). They both mark the file system busy. Lfs_vflush()
+ * explicitly marks the file system busy. So lfs_segwrite is safe. I think.
+ */
+
+int
+lfs_vflush(vp)
+ struct vnode *vp;
+{
+ struct inode *ip;
+ struct lfs *fs;
+ struct segment *sp;
+
+ fs = VFSTOUFS(vp->v_mount)->um_lfs;
+ if (fs->lfs_nactive > MAX_ACTIVE)
+ return(lfs_segwrite(vp->v_mount, SEGM_SYNC|SEGM_CKP));
+ lfs_seglock(fs, SEGM_SYNC);
+ sp = fs->lfs_sp;
+
+
+ ip = VTOI(vp);
+ if (vp->v_dirtyblkhd.lh_first == NULL)
+ lfs_writevnodes(fs, vp->v_mount, sp, VN_EMPTY);
+
+ do {
+ do {
+ if (vp->v_dirtyblkhd.lh_first != NULL)
+ lfs_writefile(fs, sp, vp);
+ } while (lfs_writeinode(fs, sp, ip));
+
+ } while (lfs_writeseg(fs, sp) && ip->i_number == LFS_IFILE_INUM);
+
+#ifdef DOSTATS
+ ++lfs_stats.nwrites;
+ if (sp->seg_flags & SEGM_SYNC)
+ ++lfs_stats.nsync_writes;
+ if (sp->seg_flags & SEGM_CKP)
+ ++lfs_stats.ncheckpoints;
+#endif
+ lfs_segunlock(fs);
+ return (0);
+}
+
+void
+lfs_writevnodes(fs, mp, sp, op)
+ struct lfs *fs;
+ struct mount *mp;
+ struct segment *sp;
+ int op;
+{
+ struct inode *ip;
+ struct vnode *vp;
+
+loop:
+ for (vp = mp->mnt_vnodelist.lh_first;
+ vp != NULL;
+ vp = vp->v_mntvnodes.le_next) {
+ /*
+ * If the vnode that we are about to sync is no longer
+ * associated with this mount point, start over.
+ */
+ if (vp->v_mount != mp)
+ goto loop;
+
+ /* XXX ignore dirops for now
+ if (op == VN_DIROP && !(vp->v_flag & VDIROP) ||
+ op != VN_DIROP && (vp->v_flag & VDIROP))
+ continue;
+ */
+
+ if (op == VN_EMPTY && vp->v_dirtyblkhd.lh_first)
+ continue;
+
+ if (vp->v_type == VNON)
+ continue;
+
+ if (lfs_vref(vp))
+ continue;
+
+ /*
+ * Write the inode/file if dirty and it's not the
+ * the IFILE.
+ */
+ ip = VTOI(vp);
+ if ((ip->i_flag &
+ (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE) ||
+ vp->v_dirtyblkhd.lh_first != NULL) &&
+ ip->i_number != LFS_IFILE_INUM) {
+ if (vp->v_dirtyblkhd.lh_first != NULL)
+ lfs_writefile(fs, sp, vp);
+ (void) lfs_writeinode(fs, sp, ip);
+ }
+ vp->v_flag &= ~VDIROP;
+ lfs_vunref(vp);
+ }
+}
+
+int
+lfs_segwrite(mp, flags)
+ struct mount *mp;
+ int flags; /* Do a checkpoint. */
+{
+ struct buf *bp;
+ struct inode *ip;
+ struct lfs *fs;
+ struct segment *sp;
+ struct vnode *vp;
+ SEGUSE *segusep;
+ daddr_t ibno;
+ CLEANERINFO *cip;
+ int clean, do_ckp, error, i;
+
+ fs = VFSTOUFS(mp)->um_lfs;
+
+ /*
+ * If we have fewer than 2 clean segments, wait until cleaner
+ * writes.
+ */
+ do {
+ LFS_CLEANERINFO(cip, fs, bp);
+ clean = cip->clean;
+ brelse(bp);
+ if (clean <= 2) {
+ printf ("segs clean: %d\n", clean);
+ wakeup(&lfs_allclean_wakeup);
+ if (error = tsleep(&fs->lfs_avail, PRIBIO + 1,
+ "lfs writer", 0))
+ return (error);
+ }
+ } while (clean <= 2 );
+
+ /*
+ * Allocate a segment structure and enough space to hold pointers to
+ * the maximum possible number of buffers which can be described in a
+ * single summary block.
+ */
+ do_ckp = flags & SEGM_CKP || fs->lfs_nactive > MAX_ACTIVE;
+ lfs_seglock(fs, flags | (do_ckp ? SEGM_CKP : 0));
+ sp = fs->lfs_sp;
+
+ lfs_writevnodes(fs, mp, sp, VN_REG);
+
+ /* XXX ignore ordering of dirops for now */
+ /* XXX
+ fs->lfs_writer = 1;
+ if (fs->lfs_dirops && (error =
+ tsleep(&fs->lfs_writer, PRIBIO + 1, "lfs writer", 0))) {
+ free(sp->bpp, M_SEGMENT);
+ free(sp, M_SEGMENT);
+ fs->lfs_writer = 0;
+ return (error);
+ }
+
+ lfs_writevnodes(fs, mp, sp, VN_DIROP);
+ */
+
+ /*
+ * If we are doing a checkpoint, mark everything since the
+ * last checkpoint as no longer ACTIVE.
+ */
+ if (do_ckp)
+ for (ibno = fs->lfs_cleansz + fs->lfs_segtabsz;
+ --ibno >= fs->lfs_cleansz; ) {
+ if (bread(fs->lfs_ivnode, ibno, fs->lfs_bsize,
+ NOCRED, &bp))
+
+ panic("lfs: ifile read");
+ segusep = (SEGUSE *)bp->b_data;
+ for (i = fs->lfs_sepb; i--; segusep++)
+ segusep->su_flags &= ~SEGUSE_ACTIVE;
+
+ error = VOP_BWRITE(bp);
+ }
+
+ if (do_ckp || fs->lfs_doifile) {
+redo:
+ vp = fs->lfs_ivnode;
+ while (vget(vp, 1));
+ ip = VTOI(vp);
+ if (vp->v_dirtyblkhd.lh_first != NULL)
+ lfs_writefile(fs, sp, vp);
+ (void)lfs_writeinode(fs, sp, ip);
+ vput(vp);
+ if (lfs_writeseg(fs, sp) && do_ckp)
+ goto redo;
+ } else
+ (void) lfs_writeseg(fs, sp);
+
+ /*
+ * If the I/O count is non-zero, sleep until it reaches zero. At the
+ * moment, the user's process hangs around so we can sleep.
+ */
+ /* XXX ignore dirops for now
+ fs->lfs_writer = 0;
+ fs->lfs_doifile = 0;
+ wakeup(&fs->lfs_dirops);
+ */
+
+#ifdef DOSTATS
+ ++lfs_stats.nwrites;
+ if (sp->seg_flags & SEGM_SYNC)
+ ++lfs_stats.nsync_writes;
+ if (sp->seg_flags & SEGM_CKP)
+ ++lfs_stats.ncheckpoints;
+#endif
+ lfs_segunlock(fs);
+ return (0);
+}
+
+/*
+ * Write the dirty blocks associated with a vnode.
+ */
+void
+lfs_writefile(fs, sp, vp)
+ struct lfs *fs;
+ struct segment *sp;
+ struct vnode *vp;
+{
+ struct buf *bp;
+ struct finfo *fip;
+ IFILE *ifp;
+
+ if (sp->seg_bytes_left < fs->lfs_bsize ||
+ sp->sum_bytes_left < sizeof(struct finfo))
+ (void) lfs_writeseg(fs, sp);
+
+ sp->sum_bytes_left -= sizeof(struct finfo) - sizeof(daddr_t);
+ ++((SEGSUM *)(sp->segsum))->ss_nfinfo;
+
+ fip = sp->fip;
+ fip->fi_nblocks = 0;
+ fip->fi_ino = VTOI(vp)->i_number;
+ LFS_IENTRY(ifp, fs, fip->fi_ino, bp);
+ fip->fi_version = ifp->if_version;
+ brelse(bp);
+
+ /*
+ * It may not be necessary to write the meta-data blocks at this point,
+ * as the roll-forward recovery code should be able to reconstruct the
+ * list.
+ */
+ lfs_gather(fs, sp, vp, lfs_match_data);
+ lfs_gather(fs, sp, vp, lfs_match_indir);
+ lfs_gather(fs, sp, vp, lfs_match_dindir);
+#ifdef TRIPLE
+ lfs_gather(fs, sp, vp, lfs_match_tindir);
+#endif
+
+ fip = sp->fip;
+ if (fip->fi_nblocks != 0) {
+ sp->fip =
+ (struct finfo *)((caddr_t)fip + sizeof(struct finfo) +
+ sizeof(daddr_t) * (fip->fi_nblocks - 1));
+ sp->start_lbp = &sp->fip->fi_blocks[0];
+ } else {
+ sp->sum_bytes_left += sizeof(struct finfo) - sizeof(daddr_t);
+ --((SEGSUM *)(sp->segsum))->ss_nfinfo;
+ }
+}
+
+int
+lfs_writeinode(fs, sp, ip)
+ struct lfs *fs;
+ struct segment *sp;
+ struct inode *ip;
+{
+ struct buf *bp, *ibp;
+ IFILE *ifp;
+ SEGUSE *sup;
+ daddr_t daddr;
+ ino_t ino;
+ int error, i, ndx;
+ int redo_ifile = 0;
+
+ if (!(ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)))
+ return(0);
+
+ /* Allocate a new inode block if necessary. */
+ if (sp->ibp == NULL) {
+ /* Allocate a new segment if necessary. */
+ if (sp->seg_bytes_left < fs->lfs_bsize ||
+ sp->sum_bytes_left < sizeof(daddr_t))
+ (void) lfs_writeseg(fs, sp);
+
+ /* Get next inode block. */
+ daddr = fs->lfs_offset;
+ fs->lfs_offset += fsbtodb(fs, 1);
+ sp->ibp = *sp->cbpp++ =
+ lfs_newbuf(VTOI(fs->lfs_ivnode)->i_devvp, daddr,
+ fs->lfs_bsize);
+ /* Zero out inode numbers */
+ for (i = 0; i < INOPB(fs); ++i)
+ ((struct dinode *)sp->ibp->b_data)[i].di_inumber = 0;
+ ++sp->start_bpp;
+ fs->lfs_avail -= fsbtodb(fs, 1);
+ /* Set remaining space counters. */
+ sp->seg_bytes_left -= fs->lfs_bsize;
+ sp->sum_bytes_left -= sizeof(daddr_t);
+ ndx = LFS_SUMMARY_SIZE / sizeof(daddr_t) -
+ sp->ninodes / INOPB(fs) - 1;
+ ((daddr_t *)(sp->segsum))[ndx] = daddr;
+ }
+
+ /* Update the inode times and copy the inode onto the inode page. */
+ if (ip->i_flag & IN_MODIFIED)
+ --fs->lfs_uinodes;
+ ITIMES(ip, &time, &time);
+ ip->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
+ bp = sp->ibp;
+ ((struct dinode *)bp->b_data)[sp->ninodes % INOPB(fs)] = ip->i_din;
+ /* Increment inode count in segment summary block. */
+ ++((SEGSUM *)(sp->segsum))->ss_ninos;
+
+ /* If this page is full, set flag to allocate a new page. */
+ if (++sp->ninodes % INOPB(fs) == 0)
+ sp->ibp = NULL;
+
+ /*
+ * If updating the ifile, update the super-block. Update the disk
+ * address and access times for this inode in the ifile.
+ */
+ ino = ip->i_number;
+ if (ino == LFS_IFILE_INUM) {
+ daddr = fs->lfs_idaddr;
+ fs->lfs_idaddr = bp->b_blkno;
+ } else {
+ LFS_IENTRY(ifp, fs, ino, ibp);
+ daddr = ifp->if_daddr;
+ ifp->if_daddr = bp->b_blkno;
+ error = VOP_BWRITE(ibp);
+ }
+
+ /*
+ * No need to update segment usage if there was no former inode address
+ * or if the last inode address is in the current partial segment.
+ */
+ if (daddr != LFS_UNUSED_DADDR &&
+ !(daddr >= fs->lfs_lastpseg && daddr <= bp->b_blkno)) {
+ LFS_SEGENTRY(sup, fs, datosn(fs, daddr), bp);
+#ifdef DIAGNOSTIC
+ if (sup->su_nbytes < sizeof(struct dinode)) {
+ /* XXX -- Change to a panic. */
+ printf("lfs: negative bytes (segment %d)\n",
+ datosn(fs, daddr));
+ panic("negative bytes");
+ }
+#endif
+ sup->su_nbytes -= sizeof(struct dinode);
+ redo_ifile =
+ (ino == LFS_IFILE_INUM && !(bp->b_flags & B_GATHERED));
+ error = VOP_BWRITE(bp);
+ }
+ return (redo_ifile);
+}
+
+int
+lfs_gatherblock(sp, bp, sptr)
+ struct segment *sp;
+ struct buf *bp;
+ int *sptr;
+{
+ struct lfs *fs;
+ int version;
+
+ /*
+ * If full, finish this segment. We may be doing I/O, so
+ * release and reacquire the splbio().
+ */
+#ifdef DIAGNOSTIC
+ if (sp->vp == NULL)
+ panic ("lfs_gatherblock: Null vp in segment");
+#endif
+ fs = sp->fs;
+ if (sp->sum_bytes_left < sizeof(daddr_t) ||
+ sp->seg_bytes_left < fs->lfs_bsize) {
+ if (sptr)
+ splx(*sptr);
+ lfs_updatemeta(sp);
+
+ version = sp->fip->fi_version;
+ (void) lfs_writeseg(fs, sp);
+
+ sp->fip->fi_version = version;
+ sp->fip->fi_ino = VTOI(sp->vp)->i_number;
+ /* Add the current file to the segment summary. */
+ ++((SEGSUM *)(sp->segsum))->ss_nfinfo;
+ sp->sum_bytes_left -=
+ sizeof(struct finfo) - sizeof(daddr_t);
+
+ if (sptr)
+ *sptr = splbio();
+ return(1);
+ }
+
+ /* Insert into the buffer list, update the FINFO block. */
+ bp->b_flags |= B_GATHERED;
+ *sp->cbpp++ = bp;
+ sp->fip->fi_blocks[sp->fip->fi_nblocks++] = bp->b_lblkno;
+
+ sp->sum_bytes_left -= sizeof(daddr_t);
+ sp->seg_bytes_left -= fs->lfs_bsize;
+ return(0);
+}
+
+void
+lfs_gather(fs, sp, vp, match)
+ struct lfs *fs;
+ struct segment *sp;
+ struct vnode *vp;
+ int (*match) __P((struct lfs *, struct buf *));
+{
+ struct buf *bp;
+ int s;
+
+ sp->vp = vp;
+ s = splbio();
+loop: for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = bp->b_vnbufs.le_next) {
+ if (bp->b_flags & B_BUSY || !match(fs, bp) ||
+ bp->b_flags & B_GATHERED)
+ continue;
+#ifdef DIAGNOSTIC
+ if (!(bp->b_flags & B_DELWRI))
+ panic("lfs_gather: bp not B_DELWRI");
+ if (!(bp->b_flags & B_LOCKED))
+ panic("lfs_gather: bp not B_LOCKED");
+#endif
+ if (lfs_gatherblock(sp, bp, &s))
+ goto loop;
+ }
+ splx(s);
+ lfs_updatemeta(sp);
+ sp->vp = NULL;
+}
+
+
+/*
+ * Update the metadata that points to the blocks listed in the FINFO
+ * array.
+ */
+void
+lfs_updatemeta(sp)
+ struct segment *sp;
+{
+ SEGUSE *sup;
+ struct buf *bp;
+ struct lfs *fs;
+ struct vnode *vp;
+ struct indir a[NIADDR + 2], *ap;
+ struct inode *ip;
+ daddr_t daddr, lbn, off;
+ int db_per_fsb, error, i, nblocks, num;
+
+ vp = sp->vp;
+ nblocks = &sp->fip->fi_blocks[sp->fip->fi_nblocks] - sp->start_lbp;
+ if (vp == NULL || nblocks == 0)
+ return;
+
+ /* Sort the blocks. */
+ if (!(sp->seg_flags & SEGM_CLEAN))
+ lfs_shellsort(sp->start_bpp, sp->start_lbp, nblocks);
+
+ /*
+ * Assign disk addresses, and update references to the logical
+ * block and the segment usage information.
+ */
+ fs = sp->fs;
+ db_per_fsb = fsbtodb(fs, 1);
+ for (i = nblocks; i--; ++sp->start_bpp) {
+ lbn = *sp->start_lbp++;
+ (*sp->start_bpp)->b_blkno = off = fs->lfs_offset;
+ fs->lfs_offset += db_per_fsb;
+
+ if (error = ufs_bmaparray(vp, lbn, &daddr, a, &num, NULL))
+ panic("lfs_updatemeta: ufs_bmaparray %d", error);
+ ip = VTOI(vp);
+ switch (num) {
+ case 0:
+ ip->i_db[lbn] = off;
+ break;
+ case 1:
+ ip->i_ib[a[0].in_off] = off;
+ break;
+ default:
+ ap = &a[num - 1];
+ if (bread(vp, ap->in_lbn, fs->lfs_bsize, NOCRED, &bp))
+ panic("lfs_updatemeta: bread bno %d",
+ ap->in_lbn);
+ /*
+ * Bread may create a new indirect block which needs
+ * to get counted for the inode.
+ */
+ if (bp->b_blkno == -1 && !(bp->b_flags & B_CACHE)) {
+printf ("Updatemeta allocating indirect block: shouldn't happen\n");
+ ip->i_blocks += btodb(fs->lfs_bsize);
+ fs->lfs_bfree -= btodb(fs->lfs_bsize);
+ }
+ ((daddr_t *)bp->b_data)[ap->in_off] = off;
+ VOP_BWRITE(bp);
+ }
+
+ /* Update segment usage information. */
+ if (daddr != UNASSIGNED &&
+ !(daddr >= fs->lfs_lastpseg && daddr <= off)) {
+ LFS_SEGENTRY(sup, fs, datosn(fs, daddr), bp);
+#ifdef DIAGNOSTIC
+ if (sup->su_nbytes < fs->lfs_bsize) {
+ /* XXX -- Change to a panic. */
+ printf("lfs: negative bytes (segment %d)\n",
+ datosn(fs, daddr));
+ panic ("Negative Bytes");
+ }
+#endif
+ sup->su_nbytes -= fs->lfs_bsize;
+ error = VOP_BWRITE(bp);
+ }
+ }
+}
+
+/*
+ * Start a new segment.
+ */
+int
+lfs_initseg(fs)
+ struct lfs *fs;
+{
+ struct segment *sp;
+ SEGUSE *sup;
+ SEGSUM *ssp;
+ struct buf *bp;
+ int repeat;
+
+ sp = fs->lfs_sp;
+
+ repeat = 0;
+ /* Advance to the next segment. */
+ if (!LFS_PARTIAL_FITS(fs)) {
+ /* Wake up any cleaning procs waiting on this file system. */
+ wakeup(&lfs_allclean_wakeup);
+
+ lfs_newseg(fs);
+ repeat = 1;
+ fs->lfs_offset = fs->lfs_curseg;
+ sp->seg_number = datosn(fs, fs->lfs_curseg);
+ sp->seg_bytes_left = fs->lfs_dbpseg * DEV_BSIZE;
+
+ /*
+ * If the segment contains a superblock, update the offset
+ * and summary address to skip over it.
+ */
+ LFS_SEGENTRY(sup, fs, sp->seg_number, bp);
+ if (sup->su_flags & SEGUSE_SUPERBLOCK) {
+ fs->lfs_offset += LFS_SBPAD / DEV_BSIZE;
+ sp->seg_bytes_left -= LFS_SBPAD;
+ }
+ brelse(bp);
+ } else {
+ sp->seg_number = datosn(fs, fs->lfs_curseg);
+ sp->seg_bytes_left = (fs->lfs_dbpseg -
+ (fs->lfs_offset - fs->lfs_curseg)) * DEV_BSIZE;
+ }
+ fs->lfs_lastpseg = fs->lfs_offset;
+
+ sp->fs = fs;
+ sp->ibp = NULL;
+ sp->ninodes = 0;
+
+ /* Get a new buffer for SEGSUM and enter it into the buffer list. */
+ sp->cbpp = sp->bpp;
+ *sp->cbpp = lfs_newbuf(VTOI(fs->lfs_ivnode)->i_devvp, fs->lfs_offset,
+ LFS_SUMMARY_SIZE);
+ sp->segsum = (*sp->cbpp)->b_data;
+ bzero(sp->segsum, LFS_SUMMARY_SIZE);
+ sp->start_bpp = ++sp->cbpp;
+ fs->lfs_offset += LFS_SUMMARY_SIZE / DEV_BSIZE;
+
+ /* Set point to SEGSUM, initialize it. */
+ ssp = sp->segsum;
+ ssp->ss_next = fs->lfs_nextseg;
+ ssp->ss_nfinfo = ssp->ss_ninos = 0;
+
+ /* Set pointer to first FINFO, initialize it. */
+ sp->fip = (struct finfo *)(sp->segsum + sizeof(SEGSUM));
+ sp->fip->fi_nblocks = 0;
+ sp->start_lbp = &sp->fip->fi_blocks[0];
+
+ sp->seg_bytes_left -= LFS_SUMMARY_SIZE;
+ sp->sum_bytes_left = LFS_SUMMARY_SIZE - sizeof(SEGSUM);
+
+ return(repeat);
+}
+
+/*
+ * Return the next segment to write.
+ */
+void
+lfs_newseg(fs)
+ struct lfs *fs;
+{
+ CLEANERINFO *cip;
+ SEGUSE *sup;
+ struct buf *bp;
+ int curseg, isdirty, sn;
+
+ LFS_SEGENTRY(sup, fs, datosn(fs, fs->lfs_nextseg), bp);
+ sup->su_flags |= SEGUSE_DIRTY | SEGUSE_ACTIVE;
+ sup->su_nbytes = 0;
+ sup->su_nsums = 0;
+ sup->su_ninos = 0;
+ (void) VOP_BWRITE(bp);
+
+ LFS_CLEANERINFO(cip, fs, bp);
+ --cip->clean;
+ ++cip->dirty;
+ (void) VOP_BWRITE(bp);
+
+ fs->lfs_lastseg = fs->lfs_curseg;
+ fs->lfs_curseg = fs->lfs_nextseg;
+ for (sn = curseg = datosn(fs, fs->lfs_curseg);;) {
+ sn = (sn + 1) % fs->lfs_nseg;
+ if (sn == curseg)
+ panic("lfs_nextseg: no clean segments");
+ LFS_SEGENTRY(sup, fs, sn, bp);
+ isdirty = sup->su_flags & SEGUSE_DIRTY;
+ brelse(bp);
+ if (!isdirty)
+ break;
+ }
+
+ ++fs->lfs_nactive;
+ fs->lfs_nextseg = sntoda(fs, sn);
+#ifdef DOSTATS
+ ++lfs_stats.segsused;
+#endif
+}
+
+int
+lfs_writeseg(fs, sp)
+ struct lfs *fs;
+ struct segment *sp;
+{
+ extern int locked_queue_count;
+ struct buf **bpp, *bp, *cbp;
+ SEGUSE *sup;
+ SEGSUM *ssp;
+ dev_t i_dev;
+ size_t size;
+ u_long *datap, *dp;
+ int ch_per_blk, do_again, i, nblocks, num, s;
+ int (*strategy)__P((struct vop_strategy_args *));
+ struct vop_strategy_args vop_strategy_a;
+ u_short ninos;
+ char *p;
+
+ /*
+ * If there are no buffers other than the segment summary to write
+ * and it is not a checkpoint, don't do anything. On a checkpoint,
+ * even if there aren't any buffers, you need to write the superblock.
+ */
+ if ((nblocks = sp->cbpp - sp->bpp) == 1)
+ return (0);
+
+ ssp = (SEGSUM *)sp->segsum;
+
+ /* Update the segment usage information. */
+ LFS_SEGENTRY(sup, fs, sp->seg_number, bp);
+ ninos = (ssp->ss_ninos + INOPB(fs) - 1) / INOPB(fs);
+ sup->su_nbytes += nblocks - 1 - ninos << fs->lfs_bshift;
+ sup->su_nbytes += ssp->ss_ninos * sizeof(struct dinode);
+ sup->su_nbytes += LFS_SUMMARY_SIZE;
+ sup->su_lastmod = time.tv_sec;
+ sup->su_ninos += ninos;
+ ++sup->su_nsums;
+ do_again = !(bp->b_flags & B_GATHERED);
+ (void)VOP_BWRITE(bp);
+ /*
+ * Compute checksum across data and then across summary; the first
+ * block (the summary block) is skipped. Set the create time here
+ * so that it's guaranteed to be later than the inode mod times.
+ *
+ * XXX
+ * Fix this to do it inline, instead of malloc/copy.
+ */
+ datap = dp = malloc(nblocks * sizeof(u_long), M_SEGMENT, M_WAITOK);
+ for (bpp = sp->bpp, i = nblocks - 1; i--;) {
+ if ((*++bpp)->b_flags & B_INVAL) {
+ if (copyin((*bpp)->b_saveaddr, dp++, sizeof(u_long)))
+ panic("lfs_writeseg: copyin failed");
+ } else
+ *dp++ = ((u_long *)(*bpp)->b_data)[0];
+ }
+ ssp->ss_create = time.tv_sec;
+ ssp->ss_datasum = cksum(datap, (nblocks - 1) * sizeof(u_long));
+ ssp->ss_sumsum =
+ cksum(&ssp->ss_datasum, LFS_SUMMARY_SIZE - sizeof(ssp->ss_sumsum));
+ free(datap, M_SEGMENT);
+#ifdef DIAGNOSTIC
+ if (fs->lfs_bfree < fsbtodb(fs, ninos) + LFS_SUMMARY_SIZE / DEV_BSIZE)
+ panic("lfs_writeseg: No diskspace for summary");
+#endif
+ fs->lfs_bfree -= (fsbtodb(fs, ninos) + LFS_SUMMARY_SIZE / DEV_BSIZE);
+
+ i_dev = VTOI(fs->lfs_ivnode)->i_dev;
+ strategy = VTOI(fs->lfs_ivnode)->i_devvp->v_op[VOFFSET(vop_strategy)];
+
+ /*
+ * When we simply write the blocks we lose a rotation for every block
+ * written. To avoid this problem, we allocate memory in chunks, copy
+ * the buffers into the chunk and write the chunk. MAXPHYS is the
+ * largest size I/O devices can handle.
+ * When the data is copied to the chunk, turn off the the B_LOCKED bit
+ * and brelse the buffer (which will move them to the LRU list). Add
+ * the B_CALL flag to the buffer header so we can count I/O's for the
+ * checkpoints and so we can release the allocated memory.
+ *
+ * XXX
+ * This should be removed if the new virtual memory system allows us to
+ * easily make the buffers contiguous in kernel memory and if that's
+ * fast enough.
+ */
+ ch_per_blk = MAXPHYS / fs->lfs_bsize;
+ for (bpp = sp->bpp, i = nblocks; i;) {
+ num = ch_per_blk;
+ if (num > i)
+ num = i;
+ i -= num;
+ size = num * fs->lfs_bsize;
+
+ cbp = lfs_newbuf(VTOI(fs->lfs_ivnode)->i_devvp,
+ (*bpp)->b_blkno, size);
+ cbp->b_dev = i_dev;
+ cbp->b_flags |= B_ASYNC | B_BUSY;
+
+ s = splbio();
+ ++fs->lfs_iocount;
+ for (p = cbp->b_data; num--;) {
+ bp = *bpp++;
+ /*
+ * Fake buffers from the cleaner are marked as B_INVAL.
+ * We need to copy the data from user space rather than
+ * from the buffer indicated.
+ * XXX == what do I do on an error?
+ */
+ if (bp->b_flags & B_INVAL) {
+ if (copyin(bp->b_saveaddr, p, bp->b_bcount))
+ panic("lfs_writeseg: copyin failed");
+ } else
+ bcopy(bp->b_data, p, bp->b_bcount);
+ p += bp->b_bcount;
+ if (bp->b_flags & B_LOCKED)
+ --locked_queue_count;
+ bp->b_flags &= ~(B_ERROR | B_READ | B_DELWRI |
+ B_LOCKED | B_GATHERED);
+ if (bp->b_flags & B_CALL) {
+ /* if B_CALL, it was created with newbuf */
+ brelvp(bp);
+ if (!(bp->b_flags & B_INVAL))
+ free(bp->b_data, M_SEGMENT);
+ free(bp, M_SEGMENT);
+ } else {
+ bremfree(bp);
+ bp->b_flags |= B_DONE;
+ reassignbuf(bp, bp->b_vp);
+ brelse(bp);
+ }
+ }
+ ++cbp->b_vp->v_numoutput;
+ splx(s);
+ cbp->b_bcount = p - (char *)cbp->b_data;
+ /*
+ * XXXX This is a gross and disgusting hack. Since these
+ * buffers are physically addressed, they hang off the
+ * device vnode (devvp). As a result, they have no way
+ * of getting to the LFS superblock or lfs structure to
+ * keep track of the number of I/O's pending. So, I am
+ * going to stuff the fs into the saveaddr field of
+ * the buffer (yuk).
+ */
+ cbp->b_saveaddr = (caddr_t)fs;
+ vop_strategy_a.a_desc = VDESC(vop_strategy);
+ vop_strategy_a.a_bp = cbp;
+ (strategy)(&vop_strategy_a);
+ }
+ /*
+ * XXX
+ * Vinvalbuf can move locked buffers off the locked queue
+ * and we have no way of knowing about this. So, after
+ * doing a big write, we recalculate how many bufers are
+ * really still left on the locked queue.
+ */
+ locked_queue_count = count_lock_queue();
+ wakeup(&locked_queue_count);
+#ifdef DOSTATS
+ ++lfs_stats.psegwrites;
+ lfs_stats.blocktot += nblocks - 1;
+ if (fs->lfs_sp->seg_flags & SEGM_SYNC)
+ ++lfs_stats.psyncwrites;
+ if (fs->lfs_sp->seg_flags & SEGM_CLEAN) {
+ ++lfs_stats.pcleanwrites;
+ lfs_stats.cleanblocks += nblocks - 1;
+ }
+#endif
+ return (lfs_initseg(fs) || do_again);
+}
+
+void
+lfs_writesuper(fs)
+ struct lfs *fs;
+{
+ struct buf *bp;
+ dev_t i_dev;
+ int (*strategy) __P((struct vop_strategy_args *));
+ int s;
+ struct vop_strategy_args vop_strategy_a;
+
+ i_dev = VTOI(fs->lfs_ivnode)->i_dev;
+ strategy = VTOI(fs->lfs_ivnode)->i_devvp->v_op[VOFFSET(vop_strategy)];
+
+ /* Checksum the superblock and copy it into a buffer. */
+ fs->lfs_cksum = cksum(fs, sizeof(struct lfs) - sizeof(fs->lfs_cksum));
+ bp = lfs_newbuf(VTOI(fs->lfs_ivnode)->i_devvp, fs->lfs_sboffs[0],
+ LFS_SBPAD);
+ *(struct lfs *)bp->b_data = *fs;
+
+ /* XXX Toggle between first two superblocks; for now just write first */
+ bp->b_dev = i_dev;
+ bp->b_flags |= B_BUSY | B_CALL | B_ASYNC;
+ bp->b_flags &= ~(B_DONE | B_ERROR | B_READ | B_DELWRI);
+ bp->b_iodone = lfs_supercallback;
+ vop_strategy_a.a_desc = VDESC(vop_strategy);
+ vop_strategy_a.a_bp = bp;
+ s = splbio();
+ ++bp->b_vp->v_numoutput;
+ splx(s);
+ (strategy)(&vop_strategy_a);
+}
+
+/*
+ * Logical block number match routines used when traversing the dirty block
+ * chain.
+ */
+int
+lfs_match_data(fs, bp)
+ struct lfs *fs;
+ struct buf *bp;
+{
+ return (bp->b_lblkno >= 0);
+}
+
+int
+lfs_match_indir(fs, bp)
+ struct lfs *fs;
+ struct buf *bp;
+{
+ int lbn;
+
+ lbn = bp->b_lblkno;
+ return (lbn < 0 && (-lbn - NDADDR) % NINDIR(fs) == 0);
+}
+
+int
+lfs_match_dindir(fs, bp)
+ struct lfs *fs;
+ struct buf *bp;
+{
+ int lbn;
+
+ lbn = bp->b_lblkno;
+ return (lbn < 0 && (-lbn - NDADDR) % NINDIR(fs) == 1);
+}
+
+int
+lfs_match_tindir(fs, bp)
+ struct lfs *fs;
+ struct buf *bp;
+{
+ int lbn;
+
+ lbn = bp->b_lblkno;
+ return (lbn < 0 && (-lbn - NDADDR) % NINDIR(fs) == 2);
+}
+
+/*
+ * Allocate a new buffer header.
+ */
+struct buf *
+lfs_newbuf(vp, daddr, size)
+ struct vnode *vp;
+ daddr_t daddr;
+ size_t size;
+{
+ struct buf *bp;
+ size_t nbytes;
+
+ nbytes = roundup(size, DEV_BSIZE);
+ bp = malloc(sizeof(struct buf), M_SEGMENT, M_WAITOK);
+ bzero(bp, sizeof(struct buf));
+ if (nbytes)
+ bp->b_data = malloc(nbytes, M_SEGMENT, M_WAITOK);
+ bgetvp(vp, bp);
+ bp->b_bufsize = size;
+ bp->b_bcount = size;
+ bp->b_lblkno = daddr;
+ bp->b_blkno = daddr;
+ bp->b_error = 0;
+ bp->b_resid = 0;
+ bp->b_iodone = lfs_callback;
+ bp->b_flags |= B_BUSY | B_CALL | B_NOCACHE;
+ return (bp);
+}
+
+void
+lfs_callback(bp)
+ struct buf *bp;
+{
+ struct lfs *fs;
+
+ fs = (struct lfs *)bp->b_saveaddr;
+#ifdef DIAGNOSTIC
+ if (fs->lfs_iocount == 0)
+ panic("lfs_callback: zero iocount\n");
+#endif
+ if (--fs->lfs_iocount == 0)
+ wakeup(&fs->lfs_iocount);
+
+ brelvp(bp);
+ free(bp->b_data, M_SEGMENT);
+ free(bp, M_SEGMENT);
+}
+
+void
+lfs_supercallback(bp)
+ struct buf *bp;
+{
+ brelvp(bp);
+ free(bp->b_data, M_SEGMENT);
+ free(bp, M_SEGMENT);
+}
+
+/*
+ * Shellsort (diminishing increment sort) from Data Structures and
+ * Algorithms, Aho, Hopcraft and Ullman, 1983 Edition, page 290;
+ * see also Knuth Vol. 3, page 84. The increments are selected from
+ * formula (8), page 95. Roughly O(N^3/2).
+ */
+/*
+ * This is our own private copy of shellsort because we want to sort
+ * two parallel arrays (the array of buffer pointers and the array of
+ * logical block numbers) simultaneously. Note that we cast the array
+ * of logical block numbers to a unsigned in this routine so that the
+ * negative block numbers (meta data blocks) sort AFTER the data blocks.
+ */
+void
+lfs_shellsort(bp_array, lb_array, nmemb)
+ struct buf **bp_array;
+ daddr_t *lb_array;
+ register int nmemb;
+{
+ static int __rsshell_increments[] = { 4, 1, 0 };
+ register int incr, *incrp, t1, t2;
+ struct buf *bp_temp;
+ u_long lb_temp;
+
+ for (incrp = __rsshell_increments; incr = *incrp++;)
+ for (t1 = incr; t1 < nmemb; ++t1)
+ for (t2 = t1 - incr; t2 >= 0;)
+ if (lb_array[t2] > lb_array[t2 + incr]) {
+ lb_temp = lb_array[t2];
+ lb_array[t2] = lb_array[t2 + incr];
+ lb_array[t2 + incr] = lb_temp;
+ bp_temp = bp_array[t2];
+ bp_array[t2] = bp_array[t2 + incr];
+ bp_array[t2 + incr] = bp_temp;
+ t2 -= incr;
+ } else
+ break;
+}
+
+/*
+ * Check VXLOCK. Return 1 if the vnode is locked. Otherwise, vget it.
+ */
+lfs_vref(vp)
+ register struct vnode *vp;
+{
+
+ if (vp->v_flag & VXLOCK)
+ return(1);
+ return (vget(vp, 0));
+}
+
+void
+lfs_vunref(vp)
+ register struct vnode *vp;
+{
+ extern int lfs_no_inactive;
+
+ /*
+ * This is vrele except that we do not want to VOP_INACTIVE
+ * this vnode. Rather than inline vrele here, we use a global
+ * flag to tell lfs_inactive not to run. Yes, its gross.
+ */
+ lfs_no_inactive = 1;
+ vrele(vp);
+ lfs_no_inactive = 0;
+}
diff --git a/sys/ufs/lfs/lfs_subr.c b/sys/ufs/lfs/lfs_subr.c
new file mode 100644
index 000000000000..afcd8c29b3f1
--- /dev/null
+++ b/sys/ufs/lfs/lfs_subr.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ *
+ * @(#)lfs_subr.c 8.2 (Berkeley) 9/21/93
+ */
+
+#include <sys/param.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/buf.h>
+#include <sys/mount.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/lfs/lfs.h>
+#include <ufs/lfs/lfs_extern.h>
+
+/*
+ * Return buffer with the contents of block "offset" from the beginning of
+ * directory "ip". If "res" is non-zero, fill it in with a pointer to the
+ * remaining space in the directory.
+ */
+int
+lfs_blkatoff(ap)
+ struct vop_blkatoff_args /* {
+ struct vnode *a_vp;
+ off_t a_offset;
+ char **a_res;
+ struct buf **a_bpp;
+ } */ *ap;
+{
+ register struct lfs *fs;
+ struct inode *ip;
+ struct buf *bp;
+ daddr_t lbn;
+ int bsize, error;
+
+ ip = VTOI(ap->a_vp);
+ fs = ip->i_lfs;
+ lbn = lblkno(fs, ap->a_offset);
+ bsize = blksize(fs);
+
+ *ap->a_bpp = NULL;
+ if (error = bread(ap->a_vp, lbn, bsize, NOCRED, &bp)) {
+ brelse(bp);
+ return (error);
+ }
+ if (ap->a_res)
+ *ap->a_res = (char *)bp->b_data + blkoff(fs, ap->a_offset);
+ *ap->a_bpp = bp;
+ return (0);
+}
+
+
+/*
+ * lfs_seglock --
+ * Single thread the segment writer.
+ */
+void
+lfs_seglock(fs, flags)
+ struct lfs *fs;
+ unsigned long flags;
+{
+ struct segment *sp;
+ int s;
+
+ if (fs->lfs_seglock)
+ if (fs->lfs_lockpid == curproc->p_pid) {
+ ++fs->lfs_seglock;
+ fs->lfs_sp->seg_flags |= flags;
+ return;
+ } else while (fs->lfs_seglock)
+ (void)tsleep(&fs->lfs_seglock, PRIBIO + 1,
+ "lfs seglock", 0);
+
+ fs->lfs_seglock = 1;
+ fs->lfs_lockpid = curproc->p_pid;
+
+ sp = fs->lfs_sp = malloc(sizeof(struct segment), M_SEGMENT, M_WAITOK);
+ sp->bpp = malloc(((LFS_SUMMARY_SIZE - sizeof(SEGSUM)) /
+ sizeof(daddr_t) + 1) * sizeof(struct buf *), M_SEGMENT, M_WAITOK);
+ sp->seg_flags = flags;
+ sp->vp = NULL;
+ (void) lfs_initseg(fs);
+
+ /*
+ * Keep a cumulative count of the outstanding I/O operations. If the
+ * disk drive catches up with us it could go to zero before we finish,
+ * so we artificially increment it by one until we've scheduled all of
+ * the writes we intend to do.
+ */
+ s = splbio();
+ ++fs->lfs_iocount;
+ splx(s);
+}
+/*
+ * lfs_segunlock --
+ * Single thread the segment writer.
+ */
+void
+lfs_segunlock(fs)
+ struct lfs *fs;
+{
+ struct segment *sp;
+ unsigned long sync, ckp;
+ int s;
+
+ if (fs->lfs_seglock == 1) {
+
+ sp = fs->lfs_sp;
+ sync = sp->seg_flags & SEGM_SYNC;
+ ckp = sp->seg_flags & SEGM_CKP;
+ if (sp->bpp != sp->cbpp) {
+ /* Free allocated segment summary */
+ fs->lfs_offset -= LFS_SUMMARY_SIZE / DEV_BSIZE;
+ brelvp(*sp->bpp);
+ free((*sp->bpp)->b_data, M_SEGMENT);
+ free(*sp->bpp, M_SEGMENT);
+ } else
+ printf ("unlock to 0 with no summary");
+ free(sp->bpp, M_SEGMENT);
+ free(sp, M_SEGMENT);
+
+ /*
+ * If the I/O count is non-zero, sleep until it reaches zero.
+ * At the moment, the user's process hangs around so we can
+ * sleep.
+ */
+ s = splbio();
+ --fs->lfs_iocount;
+ /*
+ * We let checkpoints happen asynchronously. That means
+ * that during recovery, we have to roll forward between
+ * the two segments described by the first and second
+ * superblocks to make sure that the checkpoint described
+ * by a superblock completed.
+ */
+ if (sync && fs->lfs_iocount)
+ (void)tsleep(&fs->lfs_iocount, PRIBIO + 1, "lfs vflush", 0);
+ splx(s);
+ if (ckp) {
+ fs->lfs_nactive = 0;
+ lfs_writesuper(fs);
+ }
+ --fs->lfs_seglock;
+ fs->lfs_lockpid = 0;
+ wakeup(&fs->lfs_seglock);
+ } else if (fs->lfs_seglock == 0) {
+ panic ("Seglock not held");
+ } else {
+ --fs->lfs_seglock;
+ }
+}
diff --git a/sys/ufs/lfs/lfs_syscalls.c b/sys/ufs/lfs/lfs_syscalls.c
new file mode 100644
index 000000000000..666595e6b595
--- /dev/null
+++ b/sys/ufs/lfs/lfs_syscalls.c
@@ -0,0 +1,562 @@
+/*-
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ *
+ * @(#)lfs_syscalls.c 8.5 (Berkeley) 4/20/94
+ */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/buf.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/ufs_extern.h>
+
+#include <ufs/lfs/lfs.h>
+#include <ufs/lfs/lfs_extern.h>
+#define BUMP_FIP(SP) \
+ (SP)->fip = (FINFO *) (&(SP)->fip->fi_blocks[(SP)->fip->fi_nblocks])
+
+#define INC_FINFO(SP) ++((SEGSUM *)((SP)->segsum))->ss_nfinfo
+#define DEC_FINFO(SP) --((SEGSUM *)((SP)->segsum))->ss_nfinfo
+
+/*
+ * Before committing to add something to a segment summary, make sure there
+ * is enough room. S is the bytes added to the summary.
+ */
+#define CHECK_SEG(s) \
+if (sp->sum_bytes_left < (s)) { \
+ (void) lfs_writeseg(fs, sp); \
+}
+struct buf *lfs_fakebuf __P((struct vnode *, int, size_t, caddr_t));
+
+/*
+ * lfs_markv:
+ *
+ * This will mark inodes and blocks dirty, so they are written into the log.
+ * It will block until all the blocks have been written. The segment create
+ * time passed in the block_info and inode_info structures is used to decide
+ * if the data is valid for each block (in case some process dirtied a block
+ * or inode that is being cleaned between the determination that a block is
+ * live and the lfs_markv call).
+ *
+ * 0 on success
+ * -1/errno is return on error.
+ */
+struct lfs_markv_args {
+ fsid_t *fsidp; /* file system */
+ BLOCK_INFO *blkiov; /* block array */
+ int blkcnt; /* count of block array entries */
+};
+int
+lfs_markv(p, uap, retval)
+ struct proc *p;
+ struct lfs_markv_args *uap;
+ int *retval;
+{
+ struct segment *sp;
+ BLOCK_INFO *blkp;
+ IFILE *ifp;
+ struct buf *bp, **bpp;
+ struct inode *ip;
+ struct lfs *fs;
+ struct mount *mntp;
+ struct vnode *vp;
+ fsid_t fsid;
+ void *start;
+ ino_t lastino;
+ daddr_t b_daddr, v_daddr;
+ u_long bsize;
+ int cnt, error;
+
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+
+ if (error = copyin(uap->fsidp, &fsid, sizeof(fsid_t)))
+ return (error);
+ if ((mntp = getvfs(&fsid)) == NULL)
+ return (EINVAL);
+
+ cnt = uap->blkcnt;
+ start = malloc(cnt * sizeof(BLOCK_INFO), M_SEGMENT, M_WAITOK);
+ if (error = copyin(uap->blkiov, start, cnt * sizeof(BLOCK_INFO)))
+ goto err1;
+
+ /* Mark blocks/inodes dirty. */
+ fs = VFSTOUFS(mntp)->um_lfs;
+ bsize = fs->lfs_bsize;
+ error = 0;
+
+ lfs_seglock(fs, SEGM_SYNC | SEGM_CLEAN);
+ sp = fs->lfs_sp;
+ for (v_daddr = LFS_UNUSED_DADDR, lastino = LFS_UNUSED_INUM,
+ blkp = start; cnt--; ++blkp) {
+ /*
+ * Get the IFILE entry (only once) and see if the file still
+ * exists.
+ */
+ if (lastino != blkp->bi_inode) {
+ if (lastino != LFS_UNUSED_INUM) {
+ /* Finish up last file */
+ if (sp->fip->fi_nblocks == 0) {
+ DEC_FINFO(sp);
+ sp->sum_bytes_left +=
+ sizeof(FINFO) - sizeof(daddr_t);
+ } else {
+ lfs_updatemeta(sp);
+ BUMP_FIP(sp);
+ }
+
+ lfs_writeinode(fs, sp, ip);
+ lfs_vunref(vp);
+ }
+
+ /* Start a new file */
+ CHECK_SEG(sizeof(FINFO));
+ sp->sum_bytes_left -= sizeof(FINFO) - sizeof(daddr_t);
+ INC_FINFO(sp);
+ sp->start_lbp = &sp->fip->fi_blocks[0];
+ sp->vp = NULL;
+ sp->fip->fi_version = blkp->bi_version;
+ sp->fip->fi_nblocks = 0;
+ sp->fip->fi_ino = blkp->bi_inode;
+ lastino = blkp->bi_inode;
+ if (blkp->bi_inode == LFS_IFILE_INUM)
+ v_daddr = fs->lfs_idaddr;
+ else {
+ LFS_IENTRY(ifp, fs, blkp->bi_inode, bp);
+ v_daddr = ifp->if_daddr;
+ brelse(bp);
+ }
+ if (v_daddr == LFS_UNUSED_DADDR)
+ continue;
+
+ /* Get the vnode/inode. */
+ if (lfs_fastvget(mntp, blkp->bi_inode, v_daddr, &vp,
+ blkp->bi_lbn == LFS_UNUSED_LBN ?
+ blkp->bi_bp : NULL)) {
+#ifdef DIAGNOSTIC
+ printf("lfs_markv: VFS_VGET failed (%d)\n",
+ blkp->bi_inode);
+#endif
+ lastino = LFS_UNUSED_INUM;
+ v_daddr = LFS_UNUSED_DADDR;
+ continue;
+ }
+ sp->vp = vp;
+ ip = VTOI(vp);
+ } else if (v_daddr == LFS_UNUSED_DADDR)
+ continue;
+
+ /* If this BLOCK_INFO didn't contain a block, keep going. */
+ if (blkp->bi_lbn == LFS_UNUSED_LBN)
+ continue;
+ if (VOP_BMAP(vp, blkp->bi_lbn, NULL, &b_daddr, NULL) ||
+ b_daddr != blkp->bi_daddr)
+ continue;
+ /*
+ * If we got to here, then we are keeping the block. If it
+ * is an indirect block, we want to actually put it in the
+ * buffer cache so that it can be updated in the finish_meta
+ * section. If it's not, we need to allocate a fake buffer
+ * so that writeseg can perform the copyin and write the buffer.
+ */
+ if (blkp->bi_lbn >= 0) /* Data Block */
+ bp = lfs_fakebuf(vp, blkp->bi_lbn, bsize,
+ blkp->bi_bp);
+ else {
+ bp = getblk(vp, blkp->bi_lbn, bsize, 0, 0);
+ if (!(bp->b_flags & (B_DELWRI | B_DONE | B_CACHE)) &&
+ (error = copyin(blkp->bi_bp, bp->b_data,
+ bsize)))
+ goto err2;
+ if (error = VOP_BWRITE(bp))
+ goto err2;
+ }
+ while (lfs_gatherblock(sp, bp, NULL));
+ }
+ if (sp->vp) {
+ if (sp->fip->fi_nblocks == 0) {
+ DEC_FINFO(sp);
+ sp->sum_bytes_left +=
+ sizeof(FINFO) - sizeof(daddr_t);
+ } else
+ lfs_updatemeta(sp);
+
+ lfs_writeinode(fs, sp, ip);
+ lfs_vunref(vp);
+ }
+ (void) lfs_writeseg(fs, sp);
+ lfs_segunlock(fs);
+ free(start, M_SEGMENT);
+ return (error);
+
+/*
+ * XXX
+ * If we come in to error 2, we might have indirect blocks that were
+ * updated and now have bad block pointers. I don't know what to do
+ * about this.
+ */
+
+err2: lfs_vunref(vp);
+ /* Free up fakebuffers */
+ for (bpp = --sp->cbpp; bpp >= sp->bpp; --bpp)
+ if ((*bpp)->b_flags & B_CALL) {
+ brelvp(*bpp);
+ free(*bpp, M_SEGMENT);
+ } else
+ brelse(*bpp);
+ lfs_segunlock(fs);
+err1:
+ free(start, M_SEGMENT);
+ return (error);
+}
+
+/*
+ * lfs_bmapv:
+ *
+ * This will fill in the current disk address for arrays of blocks.
+ *
+ * 0 on success
+ * -1/errno is return on error.
+ */
+struct lfs_bmapv_args {
+ fsid_t *fsidp; /* file system */
+ BLOCK_INFO *blkiov; /* block array */
+ int blkcnt; /* count of block array entries */
+};
+int
+lfs_bmapv(p, uap, retval)
+ struct proc *p;
+ struct lfs_bmapv_args *uap;
+ int *retval;
+{
+ BLOCK_INFO *blkp;
+ struct mount *mntp;
+ struct vnode *vp;
+ fsid_t fsid;
+ void *start;
+ daddr_t daddr;
+ int cnt, error, step;
+
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+
+ if (error = copyin(uap->fsidp, &fsid, sizeof(fsid_t)))
+ return (error);
+ if ((mntp = getvfs(&fsid)) == NULL)
+ return (EINVAL);
+
+ cnt = uap->blkcnt;
+ start = blkp = malloc(cnt * sizeof(BLOCK_INFO), M_SEGMENT, M_WAITOK);
+ if (error = copyin(uap->blkiov, blkp, cnt * sizeof(BLOCK_INFO))) {
+ free(blkp, M_SEGMENT);
+ return (error);
+ }
+
+ for (step = cnt; step--; ++blkp) {
+ if (blkp->bi_lbn == LFS_UNUSED_LBN)
+ continue;
+ /* Could be a deadlock ? */
+ if (VFS_VGET(mntp, blkp->bi_inode, &vp))
+ daddr = LFS_UNUSED_DADDR;
+ else {
+ if (VOP_BMAP(vp, blkp->bi_lbn, NULL, &daddr, NULL))
+ daddr = LFS_UNUSED_DADDR;
+ vput(vp);
+ }
+ blkp->bi_daddr = daddr;
+ }
+ copyout(start, uap->blkiov, cnt * sizeof(BLOCK_INFO));
+ free(start, M_SEGMENT);
+ return (0);
+}
+
+/*
+ * lfs_segclean:
+ *
+ * Mark the segment clean.
+ *
+ * 0 on success
+ * -1/errno is return on error.
+ */
+struct lfs_segclean_args {
+ fsid_t *fsidp; /* file system */
+ u_long segment; /* segment number */
+};
+int
+lfs_segclean(p, uap, retval)
+ struct proc *p;
+ struct lfs_segclean_args *uap;
+ int *retval;
+{
+ CLEANERINFO *cip;
+ SEGUSE *sup;
+ struct buf *bp;
+ struct mount *mntp;
+ struct lfs *fs;
+ fsid_t fsid;
+ int error;
+
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+
+ if (error = copyin(uap->fsidp, &fsid, sizeof(fsid_t)))
+ return (error);
+ if ((mntp = getvfs(&fsid)) == NULL)
+ return (EINVAL);
+
+ fs = VFSTOUFS(mntp)->um_lfs;
+
+ if (datosn(fs, fs->lfs_curseg) == uap->segment)
+ return (EBUSY);
+
+ LFS_SEGENTRY(sup, fs, uap->segment, bp);
+ if (sup->su_flags & SEGUSE_ACTIVE) {
+ brelse(bp);
+ return (EBUSY);
+ }
+ fs->lfs_avail += fsbtodb(fs, fs->lfs_ssize) - 1;
+ fs->lfs_bfree += (sup->su_nsums * LFS_SUMMARY_SIZE / DEV_BSIZE) +
+ sup->su_ninos * btodb(fs->lfs_bsize);
+ sup->su_flags &= ~SEGUSE_DIRTY;
+ (void) VOP_BWRITE(bp);
+
+ LFS_CLEANERINFO(cip, fs, bp);
+ ++cip->clean;
+ --cip->dirty;
+ (void) VOP_BWRITE(bp);
+ wakeup(&fs->lfs_avail);
+ return (0);
+}
+
+/*
+ * lfs_segwait:
+ *
+ * This will block until a segment in file system fsid is written. A timeout
+ * in milliseconds may be specified which will awake the cleaner automatically.
+ * An fsid of -1 means any file system, and a timeout of 0 means forever.
+ *
+ * 0 on success
+ * 1 on timeout
+ * -1/errno is return on error.
+ */
+struct lfs_segwait_args {
+ fsid_t *fsidp; /* file system */
+ struct timeval *tv; /* timeout */
+};
+int
+lfs_segwait(p, uap, retval)
+ struct proc *p;
+ struct lfs_segwait_args *uap;
+ int *retval;
+{
+ extern int lfs_allclean_wakeup;
+ struct mount *mntp;
+ struct timeval atv;
+ fsid_t fsid;
+ void *addr;
+ u_long timeout;
+ int error, s;
+
+ if (error = suser(p->p_ucred, &p->p_acflag)) {
+ return (error);
+}
+#ifdef WHEN_QUADS_WORK
+ if (error = copyin(uap->fsidp, &fsid, sizeof(fsid_t)))
+ return (error);
+ if (fsid == (fsid_t)-1)
+ addr = &lfs_allclean_wakeup;
+ else {
+ if ((mntp = getvfs(&fsid)) == NULL)
+ return (EINVAL);
+ addr = &VFSTOUFS(mntp)->um_lfs->lfs_nextseg;
+ }
+#else
+ if (error = copyin(uap->fsidp, &fsid, sizeof(fsid_t)))
+ return (error);
+ if ((mntp = getvfs(&fsid)) == NULL)
+ addr = &lfs_allclean_wakeup;
+ else
+ addr = &VFSTOUFS(mntp)->um_lfs->lfs_nextseg;
+#endif
+
+ if (uap->tv) {
+ if (error = copyin(uap->tv, &atv, sizeof(struct timeval)))
+ return (error);
+ if (itimerfix(&atv))
+ return (EINVAL);
+ s = splclock();
+ timevaladd(&atv, (struct timeval *)&time);
+ timeout = hzto(&atv);
+ splx(s);
+ } else
+ timeout = 0;
+
+ error = tsleep(addr, PCATCH | PUSER, "segment", timeout);
+ return (error == ERESTART ? EINTR : 0);
+}
+
+/*
+ * VFS_VGET call specialized for the cleaner. The cleaner already knows the
+ * daddr from the ifile, so don't look it up again. If the cleaner is
+ * processing IINFO structures, it may have the ondisk inode already, so
+ * don't go retrieving it again.
+ */
+int
+lfs_fastvget(mp, ino, daddr, vpp, dinp)
+ struct mount *mp;
+ ino_t ino;
+ daddr_t daddr;
+ struct vnode **vpp;
+ struct dinode *dinp;
+{
+ register struct inode *ip;
+ struct vnode *vp;
+ struct ufsmount *ump;
+ struct buf *bp;
+ dev_t dev;
+ int error;
+
+ ump = VFSTOUFS(mp);
+ dev = ump->um_dev;
+ /*
+ * This is playing fast and loose. Someone may have the inode
+ * locked, in which case they are going to be distinctly unhappy
+ * if we trash something.
+ */
+ if ((*vpp = ufs_ihashlookup(dev, ino)) != NULL) {
+ lfs_vref(*vpp);
+ if ((*vpp)->v_flag & VXLOCK)
+ printf ("Cleaned vnode VXLOCKED\n");
+ ip = VTOI(*vpp);
+ if (ip->i_flags & IN_LOCKED)
+ printf("cleaned vnode locked\n");
+ if (!(ip->i_flag & IN_MODIFIED)) {
+ ++ump->um_lfs->lfs_uinodes;
+ ip->i_flag |= IN_MODIFIED;
+ }
+ ip->i_flag |= IN_MODIFIED;
+ return (0);
+ }
+
+ /* Allocate new vnode/inode. */
+ if (error = lfs_vcreate(mp, ino, &vp)) {
+ *vpp = NULL;
+ return (error);
+ }
+
+ /*
+ * Put it onto its hash chain and lock it so that other requests for
+ * this inode will block if they arrive while we are sleeping waiting
+ * for old data structures to be purged or for the contents of the
+ * disk portion of this inode to be read.
+ */
+ ip = VTOI(vp);
+ ufs_ihashins(ip);
+
+ /*
+ * XXX
+ * This may not need to be here, logically it should go down with
+ * the i_devvp initialization.
+ * Ask Kirk.
+ */
+ ip->i_lfs = ump->um_lfs;
+
+ /* Read in the disk contents for the inode, copy into the inode. */
+ if (dinp)
+ if (error = copyin(dinp, &ip->i_din, sizeof(struct dinode)))
+ return (error);
+ else {
+ if (error = bread(ump->um_devvp, daddr,
+ (int)ump->um_lfs->lfs_bsize, NOCRED, &bp)) {
+ /*
+ * The inode does not contain anything useful, so it
+ * would be misleading to leave it on its hash chain.
+ * Iput() will return it to the free list.
+ */
+ ufs_ihashrem(ip);
+
+ /* Unlock and discard unneeded inode. */
+ lfs_vunref(vp);
+ brelse(bp);
+ *vpp = NULL;
+ return (error);
+ }
+ ip->i_din =
+ *lfs_ifind(ump->um_lfs, ino, (struct dinode *)bp->b_data);
+ brelse(bp);
+ }
+
+ /* Inode was just read from user space or disk, make sure it's locked */
+ ip->i_flag |= IN_LOCKED;
+
+ /*
+ * Initialize the vnode from the inode, check for aliases. In all
+ * cases re-init ip, the underlying vnode/inode may have changed.
+ */
+ if (error = ufs_vinit(mp, lfs_specop_p, LFS_FIFOOPS, &vp)) {
+ lfs_vunref(vp);
+ *vpp = NULL;
+ return (error);
+ }
+ /*
+ * Finish inode initialization now that aliasing has been resolved.
+ */
+ ip->i_devvp = ump->um_devvp;
+ ip->i_flag |= IN_MODIFIED;
+ ++ump->um_lfs->lfs_uinodes;
+ VREF(ip->i_devvp);
+ *vpp = vp;
+ return (0);
+}
+struct buf *
+lfs_fakebuf(vp, lbn, size, uaddr)
+ struct vnode *vp;
+ int lbn;
+ size_t size;
+ caddr_t uaddr;
+{
+ struct buf *bp;
+
+ bp = lfs_newbuf(vp, lbn, 0);
+ bp->b_saveaddr = uaddr;
+ bp->b_bufsize = size;
+ bp->b_bcount = size;
+ bp->b_flags |= B_INVAL;
+ return (bp);
+}
diff --git a/sys/ufs/lfs/lfs_vfsops.c b/sys/ufs/lfs/lfs_vfsops.c
new file mode 100644
index 000000000000..0c8186e2322a
--- /dev/null
+++ b/sys/ufs/lfs/lfs_vfsops.c
@@ -0,0 +1,573 @@
+/*
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ *
+ * @(#)lfs_vfsops.c 8.7 (Berkeley) 4/16/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/mbuf.h>
+#include <sys/file.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/socket.h>
+
+#include <miscfs/specfs/specdev.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/ufs_extern.h>
+
+#include <ufs/lfs/lfs.h>
+#include <ufs/lfs/lfs_extern.h>
+
+int lfs_mountfs __P((struct vnode *, struct mount *, struct proc *));
+
+struct vfsops lfs_vfsops = {
+ lfs_mount,
+ ufs_start,
+ lfs_unmount,
+ ufs_root,
+ ufs_quotactl,
+ lfs_statfs,
+ lfs_sync,
+ lfs_vget,
+ lfs_fhtovp,
+ lfs_vptofh,
+ lfs_init,
+};
+
+int
+lfs_mountroot()
+{
+ panic("lfs_mountroot"); /* XXX -- implement */
+}
+
+/*
+ * VFS Operations.
+ *
+ * mount system call
+ */
+lfs_mount(mp, path, data, ndp, p)
+ register struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ struct vnode *devvp;
+ struct ufs_args args;
+ struct ufsmount *ump;
+ register struct lfs *fs; /* LFS */
+ u_int size;
+ int error;
+
+ if (error = copyin(data, (caddr_t)&args, sizeof (struct ufs_args)))
+ return (error);
+
+ /* Until LFS can do NFS right. XXX */
+ if (args.export.ex_flags & MNT_EXPORTED)
+ return (EINVAL);
+
+ /*
+ * If updating, check whether changing from read-only to
+ * read/write; if there is no device name, that's all we do.
+ */
+ if (mp->mnt_flag & MNT_UPDATE) {
+ ump = VFSTOUFS(mp);
+#ifdef NOTLFS /* LFS */
+ fs = ump->um_fs;
+ if (fs->fs_ronly && (mp->mnt_flag & MNT_RDONLY) == 0)
+ fs->fs_ronly = 0;
+#else
+ fs = ump->um_lfs;
+ if (fs->lfs_ronly && (mp->mnt_flag & MNT_RDONLY) == 0)
+ fs->lfs_ronly = 0;
+#endif
+ if (args.fspec == 0) {
+ /*
+ * Process export requests.
+ */
+ return (vfs_export(mp, &ump->um_export, &args.export));
+ }
+ }
+ /*
+ * Not an update, or updating the name: look up the name
+ * and verify that it refers to a sensible block device.
+ */
+ NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p);
+ if (error = namei(ndp))
+ return (error);
+ devvp = ndp->ni_vp;
+ if (devvp->v_type != VBLK) {
+ vrele(devvp);
+ return (ENOTBLK);
+ }
+ if (major(devvp->v_rdev) >= nblkdev) {
+ vrele(devvp);
+ return (ENXIO);
+ }
+ if ((mp->mnt_flag & MNT_UPDATE) == 0)
+ error = lfs_mountfs(devvp, mp, p); /* LFS */
+ else {
+ if (devvp != ump->um_devvp)
+ error = EINVAL; /* needs translation */
+ else
+ vrele(devvp);
+ }
+ if (error) {
+ vrele(devvp);
+ return (error);
+ }
+ ump = VFSTOUFS(mp);
+ fs = ump->um_lfs; /* LFS */
+#ifdef NOTLFS /* LFS */
+ (void) copyinstr(path, fs->fs_fsmnt, sizeof(fs->fs_fsmnt) - 1, &size);
+ bzero(fs->fs_fsmnt + size, sizeof(fs->fs_fsmnt) - size);
+ bcopy((caddr_t)fs->fs_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname,
+ MNAMELEN);
+ (void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
+ &size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+ (void) ufs_statfs(mp, &mp->mnt_stat, p);
+#else
+ (void)copyinstr(path, fs->lfs_fsmnt, sizeof(fs->lfs_fsmnt) - 1, &size);
+ bzero(fs->lfs_fsmnt + size, sizeof(fs->lfs_fsmnt) - size);
+ bcopy((caddr_t)fs->lfs_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname,
+ MNAMELEN);
+ (void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
+ &size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+ (void) lfs_statfs(mp, &mp->mnt_stat, p);
+#endif
+ return (0);
+}
+
+/*
+ * Common code for mount and mountroot
+ * LFS specific
+ */
+int
+lfs_mountfs(devvp, mp, p)
+ register struct vnode *devvp;
+ struct mount *mp;
+ struct proc *p;
+{
+ extern struct vnode *rootvp;
+ register struct lfs *fs;
+ register struct ufsmount *ump;
+ struct vnode *vp;
+ struct buf *bp;
+ struct partinfo dpart;
+ dev_t dev;
+ int error, i, ronly, size;
+
+ /*
+ * Disallow multiple mounts of the same device.
+ * Disallow mounting of a device that is currently in use
+ * (except for root, which might share swap device for miniroot).
+ * Flush out any old buffers remaining from a previous use.
+ */
+ if (error = vfs_mountedon(devvp))
+ return (error);
+ if (vcount(devvp) > 1 && devvp != rootvp)
+ return (EBUSY);
+ if (error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0))
+ return (error);
+
+ ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
+ if (error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p))
+ return (error);
+
+ if (VOP_IOCTL(devvp, DIOCGPART, (caddr_t)&dpart, FREAD, NOCRED, p) != 0)
+ size = DEV_BSIZE;
+ else {
+ size = dpart.disklab->d_secsize;
+#ifdef NEVER_USED
+ dpart.part->p_fstype = FS_LFS;
+ dpart.part->p_fsize = fs->lfs_fsize; /* frag size */
+ dpart.part->p_frag = fs->lfs_frag; /* frags per block */
+ dpart.part->p_cpg = fs->lfs_segshift; /* segment shift */
+#endif
+ }
+
+ /* Don't free random space on error. */
+ bp = NULL;
+ ump = NULL;
+
+ /* Read in the superblock. */
+ if (error = bread(devvp, LFS_LABELPAD / size, LFS_SBPAD, NOCRED, &bp))
+ goto out;
+ fs = (struct lfs *)bp->b_data;
+
+ /* Check the basics. */
+ if (fs->lfs_magic != LFS_MAGIC || fs->lfs_bsize > MAXBSIZE ||
+ fs->lfs_bsize < sizeof(struct lfs)) {
+ error = EINVAL; /* XXX needs translation */
+ goto out;
+ }
+
+ /* Allocate the mount structure, copy the superblock into it. */
+ ump = (struct ufsmount *)malloc(sizeof *ump, M_UFSMNT, M_WAITOK);
+ fs = ump->um_lfs = malloc(sizeof(struct lfs), M_UFSMNT, M_WAITOK);
+ bcopy(bp->b_data, fs, sizeof(struct lfs));
+ if (sizeof(struct lfs) < LFS_SBPAD) /* XXX why? */
+ bp->b_flags |= B_INVAL;
+ brelse(bp);
+ bp = NULL;
+
+ /* Set up the I/O information */
+ fs->lfs_iocount = 0;
+
+ /* Set up the ifile and lock aflags */
+ fs->lfs_doifile = 0;
+ fs->lfs_writer = 0;
+ fs->lfs_dirops = 0;
+ fs->lfs_seglock = 0;
+
+ /* Set the file system readonly/modify bits. */
+ fs->lfs_ronly = ronly;
+ if (ronly == 0)
+ fs->lfs_fmod = 1;
+
+ /* Initialize the mount structure. */
+ dev = devvp->v_rdev;
+ mp->mnt_data = (qaddr_t)ump;
+ mp->mnt_stat.f_fsid.val[0] = (long)dev;
+ mp->mnt_stat.f_fsid.val[1] = MOUNT_LFS;
+ mp->mnt_flag |= MNT_LOCAL;
+ ump->um_mountp = mp;
+ ump->um_dev = dev;
+ ump->um_devvp = devvp;
+ ump->um_bptrtodb = 0;
+ ump->um_seqinc = 1 << fs->lfs_fsbtodb;
+ ump->um_nindir = fs->lfs_nindir;
+ for (i = 0; i < MAXQUOTAS; i++)
+ ump->um_quotas[i] = NULLVP;
+ devvp->v_specflags |= SI_MOUNTEDON;
+
+ /*
+ * We use the ifile vnode for almost every operation. Instead of
+ * retrieving it from the hash table each time we retrieve it here,
+ * artificially increment the reference count and keep a pointer
+ * to it in the incore copy of the superblock.
+ */
+ if (error = VFS_VGET(mp, LFS_IFILE_INUM, &vp))
+ goto out;
+ fs->lfs_ivnode = vp;
+ VREF(vp);
+ vput(vp);
+
+ return (0);
+out:
+ if (bp)
+ brelse(bp);
+ (void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, NOCRED, p);
+ if (ump) {
+ free(ump->um_lfs, M_UFSMNT);
+ free(ump, M_UFSMNT);
+ mp->mnt_data = (qaddr_t)0;
+ }
+ return (error);
+}
+
+/*
+ * unmount system call
+ */
+lfs_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ extern int doforce;
+ register struct ufsmount *ump;
+ register struct lfs *fs;
+ int i, error, flags, ronly;
+
+ flags = 0;
+ if (mntflags & MNT_FORCE) {
+ if (!doforce || (mp->mnt_flag & MNT_ROOTFS))
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+
+ ump = VFSTOUFS(mp);
+ fs = ump->um_lfs;
+#ifdef QUOTA
+ if (mp->mnt_flag & MNT_QUOTA) {
+ if (error = vflush(mp, fs->lfs_ivnode, SKIPSYSTEM|flags))
+ return (error);
+ for (i = 0; i < MAXQUOTAS; i++) {
+ if (ump->um_quotas[i] == NULLVP)
+ continue;
+ quotaoff(p, mp, i);
+ }
+ /*
+ * Here we fall through to vflush again to ensure
+ * that we have gotten rid of all the system vnodes.
+ */
+ }
+#endif
+ if (error = vflush(mp, fs->lfs_ivnode, flags))
+ return (error);
+ fs->lfs_clean = 1;
+ if (error = VFS_SYNC(mp, 1, p->p_ucred, p))
+ return (error);
+ if (fs->lfs_ivnode->v_dirtyblkhd.lh_first)
+ panic("lfs_unmount: still dirty blocks on ifile vnode\n");
+ vrele(fs->lfs_ivnode);
+ vgone(fs->lfs_ivnode);
+
+ ronly = !fs->lfs_ronly;
+ ump->um_devvp->v_specflags &= ~SI_MOUNTEDON;
+ error = VOP_CLOSE(ump->um_devvp,
+ ronly ? FREAD : FREAD|FWRITE, NOCRED, p);
+ vrele(ump->um_devvp);
+ free(fs, M_UFSMNT);
+ free(ump, M_UFSMNT);
+ mp->mnt_data = (qaddr_t)0;
+ mp->mnt_flag &= ~MNT_LOCAL;
+ return (error);
+}
+
+/*
+ * Get file system statistics.
+ */
+lfs_statfs(mp, sbp, p)
+ struct mount *mp;
+ register struct statfs *sbp;
+ struct proc *p;
+{
+ register struct lfs *fs;
+ register struct ufsmount *ump;
+
+ ump = VFSTOUFS(mp);
+ fs = ump->um_lfs;
+ if (fs->lfs_magic != LFS_MAGIC)
+ panic("lfs_statfs: magic");
+ sbp->f_type = MOUNT_LFS;
+ sbp->f_bsize = fs->lfs_bsize;
+ sbp->f_iosize = fs->lfs_bsize;
+ sbp->f_blocks = dbtofsb(fs,fs->lfs_dsize);
+ sbp->f_bfree = dbtofsb(fs, fs->lfs_bfree);
+ sbp->f_bavail = (fs->lfs_dsize * (100 - fs->lfs_minfree) / 100) -
+ (fs->lfs_dsize - fs->lfs_bfree);
+ sbp->f_bavail = dbtofsb(fs, sbp->f_bavail);
+ sbp->f_files = fs->lfs_nfiles;
+ sbp->f_ffree = sbp->f_bfree * INOPB(fs);
+ if (sbp != &mp->mnt_stat) {
+ bcopy((caddr_t)mp->mnt_stat.f_mntonname,
+ (caddr_t)&sbp->f_mntonname[0], MNAMELEN);
+ bcopy((caddr_t)mp->mnt_stat.f_mntfromname,
+ (caddr_t)&sbp->f_mntfromname[0], MNAMELEN);
+ }
+ return (0);
+}
+
+/*
+ * Go through the disk queues to initiate sandbagged IO;
+ * go through the inodes to write those that have been modified;
+ * initiate the writing of the super block if it has been modified.
+ *
+ * Note: we are always called with the filesystem marked `MPBUSY'.
+ */
+lfs_sync(mp, waitfor, cred, p)
+ struct mount *mp;
+ int waitfor;
+ struct ucred *cred;
+ struct proc *p;
+{
+ int error;
+
+ /* All syncs must be checkpoints until roll-forward is implemented. */
+ error = lfs_segwrite(mp, SEGM_CKP | (waitfor ? SEGM_SYNC : 0));
+#ifdef QUOTA
+ qsync(mp);
+#endif
+ return (error);
+}
+
+/*
+ * Look up an LFS dinode number to find its incore vnode. If not already
+ * in core, read it in from the specified device. Return the inode locked.
+ * Detection and handling of mount points must be done by the calling routine.
+ */
+int
+lfs_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+ register struct lfs *fs;
+ register struct inode *ip;
+ struct buf *bp;
+ struct ifile *ifp;
+ struct vnode *vp;
+ struct ufsmount *ump;
+ daddr_t daddr;
+ dev_t dev;
+ int error;
+
+ ump = VFSTOUFS(mp);
+ dev = ump->um_dev;
+ if ((*vpp = ufs_ihashget(dev, ino)) != NULL)
+ return (0);
+
+ /* Translate the inode number to a disk address. */
+ fs = ump->um_lfs;
+ if (ino == LFS_IFILE_INUM)
+ daddr = fs->lfs_idaddr;
+ else {
+ LFS_IENTRY(ifp, fs, ino, bp);
+ daddr = ifp->if_daddr;
+ brelse(bp);
+ if (daddr == LFS_UNUSED_DADDR)
+ return (ENOENT);
+ }
+
+ /* Allocate new vnode/inode. */
+ if (error = lfs_vcreate(mp, ino, &vp)) {
+ *vpp = NULL;
+ return (error);
+ }
+
+ /*
+ * Put it onto its hash chain and lock it so that other requests for
+ * this inode will block if they arrive while we are sleeping waiting
+ * for old data structures to be purged or for the contents of the
+ * disk portion of this inode to be read.
+ */
+ ip = VTOI(vp);
+ ufs_ihashins(ip);
+
+ /*
+ * XXX
+ * This may not need to be here, logically it should go down with
+ * the i_devvp initialization.
+ * Ask Kirk.
+ */
+ ip->i_lfs = ump->um_lfs;
+
+ /* Read in the disk contents for the inode, copy into the inode. */
+ if (error =
+ bread(ump->um_devvp, daddr, (int)fs->lfs_bsize, NOCRED, &bp)) {
+ /*
+ * The inode does not contain anything useful, so it would
+ * be misleading to leave it on its hash chain. With mode
+ * still zero, it will be unlinked and returned to the free
+ * list by vput().
+ */
+ vput(vp);
+ brelse(bp);
+ *vpp = NULL;
+ return (error);
+ }
+ ip->i_din = *lfs_ifind(fs, ino, (struct dinode *)bp->b_data);
+ brelse(bp);
+
+ /*
+ * Initialize the vnode from the inode, check for aliases. In all
+ * cases re-init ip, the underlying vnode/inode may have changed.
+ */
+ if (error = ufs_vinit(mp, lfs_specop_p, LFS_FIFOOPS, &vp)) {
+ vput(vp);
+ *vpp = NULL;
+ return (error);
+ }
+ /*
+ * Finish inode initialization now that aliasing has been resolved.
+ */
+ ip->i_devvp = ump->um_devvp;
+ VREF(ip->i_devvp);
+ *vpp = vp;
+ return (0);
+}
+
+/*
+ * File handle to vnode
+ *
+ * Have to be really careful about stale file handles:
+ * - check that the inode number is valid
+ * - call lfs_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
+ *
+ * XXX
+ * use ifile to see if inode is allocated instead of reading off disk
+ * what is the relationship between my generational number and the NFS
+ * generational number.
+ */
+int
+lfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp)
+ register struct mount *mp;
+ struct fid *fhp;
+ struct mbuf *nam;
+ struct vnode **vpp;
+ int *exflagsp;
+ struct ucred **credanonp;
+{
+ register struct ufid *ufhp;
+
+ ufhp = (struct ufid *)fhp;
+ if (ufhp->ufid_ino < ROOTINO)
+ return (ESTALE);
+ return (ufs_check_export(mp, ufhp, nam, vpp, exflagsp, credanonp));
+}
+
+/*
+ * Vnode pointer to File handle
+ */
+/* ARGSUSED */
+lfs_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+ register struct inode *ip;
+ register struct ufid *ufhp;
+
+ ip = VTOI(vp);
+ ufhp = (struct ufid *)fhp;
+ ufhp->ufid_len = sizeof(struct ufid);
+ ufhp->ufid_ino = ip->i_number;
+ ufhp->ufid_gen = ip->i_gen;
+ return (0);
+}
diff --git a/sys/ufs/lfs/lfs_vnops.c b/sys/ufs/lfs/lfs_vnops.c
new file mode 100644
index 000000000000..fc6bd480d22a
--- /dev/null
+++ b/sys/ufs/lfs/lfs_vnops.c
@@ -0,0 +1,487 @@
+/*
+ * Copyright (c) 1986, 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ *
+ * @(#)lfs_vnops.c 8.5 (Berkeley) 12/30/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/resourcevar.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/conf.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+
+#include <vm/vm.h>
+
+#include <miscfs/specfs/specdev.h>
+#include <miscfs/fifofs/fifo.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/ufs_extern.h>
+
+#include <ufs/lfs/lfs.h>
+#include <ufs/lfs/lfs_extern.h>
+
+/* Global vfs data structures for lfs. */
+int (**lfs_vnodeop_p)();
+struct vnodeopv_entry_desc lfs_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, ufs_lookup }, /* lookup */
+ { &vop_create_desc, ufs_create }, /* create */
+ { &vop_mknod_desc, ufs_mknod }, /* mknod */
+ { &vop_open_desc, ufs_open }, /* open */
+ { &vop_close_desc, lfs_close }, /* close */
+ { &vop_access_desc, ufs_access }, /* access */
+ { &vop_getattr_desc, lfs_getattr }, /* getattr */
+ { &vop_setattr_desc, ufs_setattr }, /* setattr */
+ { &vop_read_desc, lfs_read }, /* read */
+ { &vop_write_desc, lfs_write }, /* write */
+ { &vop_ioctl_desc, ufs_ioctl }, /* ioctl */
+ { &vop_select_desc, ufs_select }, /* select */
+ { &vop_mmap_desc, ufs_mmap }, /* mmap */
+ { &vop_fsync_desc, lfs_fsync }, /* fsync */
+ { &vop_seek_desc, ufs_seek }, /* seek */
+ { &vop_remove_desc, ufs_remove }, /* remove */
+ { &vop_link_desc, ufs_link }, /* link */
+ { &vop_rename_desc, ufs_rename }, /* rename */
+ { &vop_mkdir_desc, ufs_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, ufs_rmdir }, /* rmdir */
+ { &vop_symlink_desc, ufs_symlink }, /* symlink */
+ { &vop_readdir_desc, ufs_readdir }, /* readdir */
+ { &vop_readlink_desc, ufs_readlink }, /* readlink */
+ { &vop_abortop_desc, ufs_abortop }, /* abortop */
+ { &vop_inactive_desc, lfs_inactive }, /* inactive */
+ { &vop_reclaim_desc, ufs_reclaim }, /* reclaim */
+ { &vop_lock_desc, ufs_lock }, /* lock */
+ { &vop_unlock_desc, ufs_unlock }, /* unlock */
+ { &vop_bmap_desc, ufs_bmap }, /* bmap */
+ { &vop_strategy_desc, ufs_strategy }, /* strategy */
+ { &vop_print_desc, ufs_print }, /* print */
+ { &vop_islocked_desc, ufs_islocked }, /* islocked */
+ { &vop_pathconf_desc, ufs_pathconf }, /* pathconf */
+ { &vop_advlock_desc, ufs_advlock }, /* advlock */
+ { &vop_blkatoff_desc, lfs_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, lfs_valloc }, /* valloc */
+ { &vop_vfree_desc, lfs_vfree }, /* vfree */
+ { &vop_truncate_desc, lfs_truncate }, /* truncate */
+ { &vop_update_desc, lfs_update }, /* update */
+ { &vop_bwrite_desc, lfs_bwrite }, /* bwrite */
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc lfs_vnodeop_opv_desc =
+ { &lfs_vnodeop_p, lfs_vnodeop_entries };
+
+int (**lfs_specop_p)();
+struct vnodeopv_entry_desc lfs_specop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, spec_lookup }, /* lookup */
+ { &vop_create_desc, spec_create }, /* create */
+ { &vop_mknod_desc, spec_mknod }, /* mknod */
+ { &vop_open_desc, spec_open }, /* open */
+ { &vop_close_desc, ufsspec_close }, /* close */
+ { &vop_access_desc, ufs_access }, /* access */
+ { &vop_getattr_desc, lfs_getattr }, /* getattr */
+ { &vop_setattr_desc, ufs_setattr }, /* setattr */
+ { &vop_read_desc, ufsspec_read }, /* read */
+ { &vop_write_desc, ufsspec_write }, /* write */
+ { &vop_ioctl_desc, spec_ioctl }, /* ioctl */
+ { &vop_select_desc, spec_select }, /* select */
+ { &vop_mmap_desc, spec_mmap }, /* mmap */
+ { &vop_fsync_desc, spec_fsync }, /* fsync */
+ { &vop_seek_desc, spec_seek }, /* seek */
+ { &vop_remove_desc, spec_remove }, /* remove */
+ { &vop_link_desc, spec_link }, /* link */
+ { &vop_rename_desc, spec_rename }, /* rename */
+ { &vop_mkdir_desc, spec_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, spec_rmdir }, /* rmdir */
+ { &vop_symlink_desc, spec_symlink }, /* symlink */
+ { &vop_readdir_desc, spec_readdir }, /* readdir */
+ { &vop_readlink_desc, spec_readlink }, /* readlink */
+ { &vop_abortop_desc, spec_abortop }, /* abortop */
+ { &vop_inactive_desc, lfs_inactive }, /* inactive */
+ { &vop_reclaim_desc, ufs_reclaim }, /* reclaim */
+ { &vop_lock_desc, ufs_lock }, /* lock */
+ { &vop_unlock_desc, ufs_unlock }, /* unlock */
+ { &vop_bmap_desc, spec_bmap }, /* bmap */
+ { &vop_strategy_desc, spec_strategy }, /* strategy */
+ { &vop_print_desc, ufs_print }, /* print */
+ { &vop_islocked_desc, ufs_islocked }, /* islocked */
+ { &vop_pathconf_desc, spec_pathconf }, /* pathconf */
+ { &vop_advlock_desc, spec_advlock }, /* advlock */
+ { &vop_blkatoff_desc, spec_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, spec_valloc }, /* valloc */
+ { &vop_vfree_desc, lfs_vfree }, /* vfree */
+ { &vop_truncate_desc, spec_truncate }, /* truncate */
+ { &vop_update_desc, lfs_update }, /* update */
+ { &vop_bwrite_desc, lfs_bwrite }, /* bwrite */
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc lfs_specop_opv_desc =
+ { &lfs_specop_p, lfs_specop_entries };
+
+#ifdef FIFO
+int (**lfs_fifoop_p)();
+struct vnodeopv_entry_desc lfs_fifoop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, fifo_lookup }, /* lookup */
+ { &vop_create_desc, fifo_create }, /* create */
+ { &vop_mknod_desc, fifo_mknod }, /* mknod */
+ { &vop_open_desc, fifo_open }, /* open */
+ { &vop_close_desc, ufsfifo_close }, /* close */
+ { &vop_access_desc, ufs_access }, /* access */
+ { &vop_getattr_desc, lfs_getattr }, /* getattr */
+ { &vop_setattr_desc, ufs_setattr }, /* setattr */
+ { &vop_read_desc, ufsfifo_read }, /* read */
+ { &vop_write_desc, ufsfifo_write }, /* write */
+ { &vop_ioctl_desc, fifo_ioctl }, /* ioctl */
+ { &vop_select_desc, fifo_select }, /* select */
+ { &vop_mmap_desc, fifo_mmap }, /* mmap */
+ { &vop_fsync_desc, fifo_fsync }, /* fsync */
+ { &vop_seek_desc, fifo_seek }, /* seek */
+ { &vop_remove_desc, fifo_remove }, /* remove */
+ { &vop_link_desc, fifo_link }, /* link */
+ { &vop_rename_desc, fifo_rename }, /* rename */
+ { &vop_mkdir_desc, fifo_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, fifo_rmdir }, /* rmdir */
+ { &vop_symlink_desc, fifo_symlink }, /* symlink */
+ { &vop_readdir_desc, fifo_readdir }, /* readdir */
+ { &vop_readlink_desc, fifo_readlink }, /* readlink */
+ { &vop_abortop_desc, fifo_abortop }, /* abortop */
+ { &vop_inactive_desc, lfs_inactive }, /* inactive */
+ { &vop_reclaim_desc, ufs_reclaim }, /* reclaim */
+ { &vop_lock_desc, ufs_lock }, /* lock */
+ { &vop_unlock_desc, ufs_unlock }, /* unlock */
+ { &vop_bmap_desc, fifo_bmap }, /* bmap */
+ { &vop_strategy_desc, fifo_strategy }, /* strategy */
+ { &vop_print_desc, ufs_print }, /* print */
+ { &vop_islocked_desc, ufs_islocked }, /* islocked */
+ { &vop_pathconf_desc, fifo_pathconf }, /* pathconf */
+ { &vop_advlock_desc, fifo_advlock }, /* advlock */
+ { &vop_blkatoff_desc, fifo_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, fifo_valloc }, /* valloc */
+ { &vop_vfree_desc, lfs_vfree }, /* vfree */
+ { &vop_truncate_desc, fifo_truncate }, /* truncate */
+ { &vop_update_desc, lfs_update }, /* update */
+ { &vop_bwrite_desc, lfs_bwrite }, /* bwrite */
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc lfs_fifoop_opv_desc =
+ { &lfs_fifoop_p, lfs_fifoop_entries };
+#endif /* FIFO */
+
+#define LFS_READWRITE
+#include <ufs/ufs/ufs_readwrite.c>
+#undef LFS_READWRITE
+
+/*
+ * Synch an open file.
+ */
+/* ARGSUSED */
+lfs_fsync(ap)
+ struct vop_fsync_args /* {
+ struct vnode *a_vp;
+ struct ucred *a_cred;
+ int a_waitfor;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct timeval tv;
+
+ tv = time;
+ return (VOP_UPDATE(ap->a_vp, &tv, &tv,
+ ap->a_waitfor == MNT_WAIT ? LFS_SYNC : 0));
+}
+
+/*
+ * These macros are used to bracket UFS directory ops, so that we can
+ * identify all the pages touched during directory ops which need to
+ * be ordered and flushed atomically, so that they may be recovered.
+ */
+#define SET_DIROP(fs) { \
+ if ((fs)->lfs_writer) \
+ tsleep(&(fs)->lfs_dirops, PRIBIO + 1, "lfs_dirop", 0); \
+ ++(fs)->lfs_dirops; \
+ (fs)->lfs_doifile = 1; \
+}
+
+#define SET_ENDOP(fs) { \
+ --(fs)->lfs_dirops; \
+ if (!(fs)->lfs_dirops) \
+ wakeup(&(fs)->lfs_writer); \
+}
+
+#define MARK_VNODE(dvp) (dvp)->v_flag |= VDIROP
+
+int
+lfs_symlink(ap)
+ struct vop_symlink_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ char *a_target;
+ } */ *ap;
+{
+ int ret;
+
+ SET_DIROP(VTOI(ap->a_dvp)->i_lfs);
+ MARK_VNODE(ap->a_dvp);
+ ret = ufs_symlink(ap);
+ SET_ENDOP(VTOI(ap->a_dvp)->i_lfs);
+ return (ret);
+}
+
+int
+lfs_mknod(ap)
+ struct vop_mknod_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ int ret;
+
+ SET_DIROP(VTOI(ap->a_dvp)->i_lfs);
+ MARK_VNODE(ap->a_dvp);
+ ret = ufs_mknod(ap);
+ SET_ENDOP(VTOI(ap->a_dvp)->i_lfs);
+ return (ret);
+}
+
+int
+lfs_create(ap)
+ struct vop_create_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ int ret;
+
+ SET_DIROP(VTOI(ap->a_dvp)->i_lfs);
+ MARK_VNODE(ap->a_dvp);
+ ret = ufs_create(ap);
+ SET_ENDOP(VTOI(ap->a_dvp)->i_lfs);
+ return (ret);
+}
+
+int
+lfs_mkdir(ap)
+ struct vop_mkdir_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ int ret;
+
+ SET_DIROP(VTOI(ap->a_dvp)->i_lfs);
+ MARK_VNODE(ap->a_dvp);
+ ret = ufs_mkdir(ap);
+ SET_ENDOP(VTOI(ap->a_dvp)->i_lfs);
+ return (ret);
+}
+
+int
+lfs_remove(ap)
+ struct vop_remove_args /* {
+ struct vnode *a_dvp;
+ struct vnode *a_vp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ int ret;
+
+ SET_DIROP(VTOI(ap->a_dvp)->i_lfs);
+ MARK_VNODE(ap->a_dvp);
+ MARK_VNODE(ap->a_vp);
+ ret = ufs_remove(ap);
+ SET_ENDOP(VTOI(ap->a_dvp)->i_lfs);
+ return (ret);
+}
+
+int
+lfs_rmdir(ap)
+ struct vop_rmdir_args /* {
+ struct vnodeop_desc *a_desc;
+ struct vnode *a_dvp;
+ struct vnode *a_vp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ int ret;
+
+ SET_DIROP(VTOI(ap->a_dvp)->i_lfs);
+ MARK_VNODE(ap->a_dvp);
+ MARK_VNODE(ap->a_vp);
+ ret = ufs_rmdir(ap);
+ SET_ENDOP(VTOI(ap->a_dvp)->i_lfs);
+ return (ret);
+}
+
+int
+lfs_link(ap)
+ struct vop_link_args /* {
+ struct vnode *a_vp;
+ struct vnode *a_tdvp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ int ret;
+
+ SET_DIROP(VTOI(ap->a_vp)->i_lfs);
+ MARK_VNODE(ap->a_vp);
+ ret = ufs_link(ap);
+ SET_ENDOP(VTOI(ap->a_vp)->i_lfs);
+ return (ret);
+}
+
+int
+lfs_rename(ap)
+ struct vop_rename_args /* {
+ struct vnode *a_fdvp;
+ struct vnode *a_fvp;
+ struct componentname *a_fcnp;
+ struct vnode *a_tdvp;
+ struct vnode *a_tvp;
+ struct componentname *a_tcnp;
+ } */ *ap;
+{
+ int ret;
+
+ SET_DIROP(VTOI(ap->a_fdvp)->i_lfs);
+ MARK_VNODE(ap->a_fdvp);
+ MARK_VNODE(ap->a_tdvp);
+ ret = ufs_rename(ap);
+ SET_ENDOP(VTOI(ap->a_fdvp)->i_lfs);
+ return (ret);
+}
+/* XXX hack to avoid calling ITIMES in getattr */
+int
+lfs_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct inode *ip = VTOI(vp);
+ register struct vattr *vap = ap->a_vap;
+ /*
+ * Copy from inode table
+ */
+ vap->va_fsid = ip->i_dev;
+ vap->va_fileid = ip->i_number;
+ vap->va_mode = ip->i_mode & ~IFMT;
+ vap->va_nlink = ip->i_nlink;
+ vap->va_uid = ip->i_uid;
+ vap->va_gid = ip->i_gid;
+ vap->va_rdev = (dev_t)ip->i_rdev;
+ vap->va_size = ip->i_din.di_size;
+ vap->va_atime = ip->i_atime;
+ vap->va_mtime = ip->i_mtime;
+ vap->va_ctime = ip->i_ctime;
+ vap->va_flags = ip->i_flags;
+ vap->va_gen = ip->i_gen;
+ /* this doesn't belong here */
+ if (vp->v_type == VBLK)
+ vap->va_blocksize = BLKDEV_IOSIZE;
+ else if (vp->v_type == VCHR)
+ vap->va_blocksize = MAXBSIZE;
+ else
+ vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize;
+ vap->va_bytes = dbtob(ip->i_blocks);
+ vap->va_type = vp->v_type;
+ vap->va_filerev = ip->i_modrev;
+ return (0);
+}
+/*
+ * Close called
+ *
+ * XXX -- we were using ufs_close, but since it updates the
+ * times on the inode, we might need to bump the uinodes
+ * count.
+ */
+/* ARGSUSED */
+int
+lfs_close(ap)
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct inode *ip = VTOI(vp);
+ int mod;
+
+ if (vp->v_usecount > 1 && !(ip->i_flag & IN_LOCKED)) {
+ mod = ip->i_flag & IN_MODIFIED;
+ ITIMES(ip, &time, &time);
+ if (!mod && ip->i_flag & IN_MODIFIED)
+ ip->i_lfs->lfs_uinodes++;
+ }
+ return (0);
+}
+
+/*
+ * Stub inactive routine that avoid calling ufs_inactive in some cases.
+ */
+int lfs_no_inactive = 0;
+
+int
+lfs_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ if (lfs_no_inactive)
+ return (0);
+ return (ufs_inactive(ap));
+}