From 1f1db49fd98099559dc758fa49e0da4a9637ea13 Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Tue, 4 Apr 2006 17:34:32 +0000 Subject: [PATCH] Transplant all the UFS ops that EXT2 used to call into the EXT2 tree and reconnect it to the build. Recent BUF/BIO work made most of the UFS tree incompatible with EXT2FS. Reported-by: Csaba Henk --- sys/conf/files | 13 +- sys/sys/vfscache.h | 4 +- sys/vfs/Makefile | 4 +- sys/vfs/gnu/ext2fs/COPYRIGHT.INFO | 23 +- sys/vfs/gnu/ext2fs/Makefile | 5 +- sys/vfs/gnu/ext2fs/dinode.h | 138 +++ sys/vfs/gnu/ext2fs/dir.h | 160 +++ sys/vfs/gnu/ext2fs/ext2_alloc.c | 20 +- sys/vfs/gnu/ext2fs/ext2_balloc.c | 12 +- sys/vfs/gnu/ext2fs/ext2_bmap.c | 338 +++++++ sys/vfs/gnu/ext2fs/ext2_extern.h | 34 +- sys/vfs/gnu/ext2fs/ext2_ihash.c | 231 +++++ sys/vfs/gnu/ext2fs/ext2_inode.c | 131 ++- sys/vfs/gnu/ext2fs/ext2_inode_cnv.c | 33 +- sys/vfs/gnu/ext2fs/ext2_linux_balloc.c | 48 +- sys/vfs/gnu/ext2fs/ext2_linux_ialloc.c | 24 +- sys/vfs/gnu/ext2fs/ext2_lookup.c | 47 +- sys/vfs/gnu/ext2fs/ext2_quota.c | 940 ++++++++++++++++++ sys/vfs/gnu/ext2fs/ext2_readwrite.c | 6 +- sys/vfs/gnu/ext2fs/ext2_subr.c | 13 +- sys/vfs/gnu/ext2fs/ext2_vfsops.c | 274 ++++-- sys/vfs/gnu/ext2fs/ext2_vnops.c | 1235 ++++++++++++++++++++++-- sys/vfs/gnu/ext2fs/ext2mount.h | 126 +++ sys/vfs/gnu/ext2fs/fs.h | 6 +- sys/vfs/gnu/ext2fs/i386-bitops.h | 12 +- sys/vfs/gnu/ext2fs/inode.h | 175 ++++ sys/vfs/gnu/ext2fs/quota.h | 205 ++++ 27 files changed, 3974 insertions(+), 283 deletions(-) create mode 100644 sys/vfs/gnu/ext2fs/dinode.h create mode 100644 sys/vfs/gnu/ext2fs/dir.h create mode 100644 sys/vfs/gnu/ext2fs/ext2_bmap.c create mode 100644 sys/vfs/gnu/ext2fs/ext2_ihash.c create mode 100644 sys/vfs/gnu/ext2fs/ext2_quota.c create mode 100644 sys/vfs/gnu/ext2fs/ext2mount.h create mode 100644 sys/vfs/gnu/ext2fs/inode.h create mode 100644 sys/vfs/gnu/ext2fs/quota.h diff --git a/sys/conf/files b/sys/conf/files index e1905d1f76..0d37169d5a 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,5 +1,5 @@ # $FreeBSD: src/sys/conf/files,v 1.340.2.137 2003/06/04 17:10:30 sam Exp $ -# $DragonFly: src/sys/conf/files,v 1.117 2006/04/01 10:06:39 swildner Exp $ +# $DragonFly: src/sys/conf/files,v 1.118 2006/04/04 17:34:23 dillon Exp $ # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and @@ -389,15 +389,18 @@ dev/netif/wi/if_wi_pci.c optional wi pci dev/netif/xe/if_xe.c optional xe dev/netif/xe/if_xe_pccard.c optional xe pccard vfs/gnu/ext2fs/ext2_alloc.c optional ext2fs -vfs/gnu/ext2fs/ext2_balloc.c optional ext2fs +vfs/gnu/ext2fs/ext2_balloc.c optional ext2fs +vfs/gnu/ext2fs/ext2_bmap.c optional ext2fs vfs/gnu/ext2fs/ext2_inode.c optional ext2fs -vfs/gnu/ext2fs/ext2_inode_cnv.c optional ext2fs +vfs/gnu/ext2fs/ext2_inode_cnv.c optional ext2fs vfs/gnu/ext2fs/ext2_linux_balloc.c optional ext2fs vfs/gnu/ext2fs/ext2_linux_ialloc.c optional ext2fs -vfs/gnu/ext2fs/ext2_lookup.c optional ext2fs +vfs/gnu/ext2fs/ext2_lookup.c optional ext2fs vfs/gnu/ext2fs/ext2_subr.c optional ext2fs -vfs/gnu/ext2fs/ext2_vfsops.c optional ext2fs +vfs/gnu/ext2fs/ext2_vfsops.c optional ext2fs vfs/gnu/ext2fs/ext2_vnops.c optional ext2fs +vfs/gnu/ext2fs/ext2_ihash.c optional ext2fs +vfs/gnu/ext2fs/ext2_quota.c optional ext2fs # # isdn4bsd device drivers # diff --git a/sys/sys/vfscache.h b/sys/sys/vfscache.h index a46ebc42ee..faa9b40270 100644 --- a/sys/sys/vfscache.h +++ b/sys/sys/vfscache.h @@ -63,7 +63,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $DragonFly: src/sys/sys/vfscache.h,v 1.3 2005/09/17 07:43:01 dillon Exp $ + * $DragonFly: src/sys/sys/vfscache.h,v 1.4 2006/04/04 17:34:31 dillon Exp $ */ /* * This module serves as a focal point for virtually all filesystem and @@ -102,7 +102,7 @@ enum vtagtype { VT_NON, VT_UFS, VT_NFS, VT_MFS, VT_PC, VT_LFS, VT_LOFS, VT_FDESC, VT_PORTAL, VT_NULL, VT_UMAP, VT_KERNFS, VT_PROCFS, VT_AFS, VT_ISOFS, VT_UNION, VT_MSDOSFS, VT_TFS, VT_VFS, VT_CODA, VT_NTFS, - VT_HPFS, VT_NWFS, VT_SMBFS, VT_UDF + VT_HPFS, VT_NWFS, VT_SMBFS, VT_UDF, VT_EXT2FS }; /* diff --git a/sys/vfs/Makefile b/sys/vfs/Makefile index 4991376592..d0216e65f5 100644 --- a/sys/vfs/Makefile +++ b/sys/vfs/Makefile @@ -1,8 +1,8 @@ # Makefile for vfs modules # -# $DragonFly: src/sys/vfs/Makefile,v 1.5 2006/04/03 02:02:31 dillon Exp $ +# $DragonFly: src/sys/vfs/Makefile,v 1.6 2006/04/04 17:34:32 dillon Exp $ -SUBDIR=fifofs msdosfs portal nfs procfs \ +SUBDIR=fifofs msdosfs portal gnu nfs procfs \ coda hpfs ntfs smbfs isofs fdesc mfs nwfs udf \ nullfs diff --git a/sys/vfs/gnu/ext2fs/COPYRIGHT.INFO b/sys/vfs/gnu/ext2fs/COPYRIGHT.INFO index 056dfda256..1b2be1a757 100644 --- a/sys/vfs/gnu/ext2fs/COPYRIGHT.INFO +++ b/sys/vfs/gnu/ext2fs/COPYRIGHT.INFO @@ -1,5 +1,5 @@ $FreeBSD: src/sys/gnu/ext2fs/COPYRIGHT.INFO,v 1.3 2000/01/01 11:27:50 bde Exp $ -$DragonFly: src/sys/vfs/gnu/ext2fs/COPYRIGHT.INFO,v 1.2 2003/06/17 04:28:33 dillon Exp $ +$DragonFly: src/sys/vfs/gnu/ext2fs/COPYRIGHT.INFO,v 1.3 2006/04/04 17:34:32 dillon Exp $ Most of the files in this directory are written by Godmar Back or modified by him using the CSRG sources. Those files are covered by the Berkeley-style @@ -34,3 +34,24 @@ control of his code.... John dyson@freebsd.org + ----------------------------------------- + 04 April 2006 + + EXT2 has been divorced from UFS. The following files have been copied + from the UFS tree and renamed: + + dinode.h + dir.h + ext2_bmap.c + ext2_ihash.c + ext2_quota.c + ext2mount.h + inode.h + quota.h + + In addition, all other UFS procedures that EXT2 used to call, + especially in ext2_vnops.c and ext2_vfsops.c, have been copied from + the UFS tree into the EXT2 tree and renamed. + + Matthew Dillon + diff --git a/sys/vfs/gnu/ext2fs/Makefile b/sys/vfs/gnu/ext2fs/Makefile index 824c121923..feb1b09a94 100644 --- a/sys/vfs/gnu/ext2fs/Makefile +++ b/sys/vfs/gnu/ext2fs/Makefile @@ -1,12 +1,13 @@ # $FreeBSD: src/sys/modules/ext2fs/Makefile,v 1.4.2.1 2002/08/06 14:14:25 mdodd Exp $ -# $DragonFly: src/sys/vfs/gnu/ext2fs/Makefile,v 1.3 2004/08/13 17:51:10 dillon Exp $ +# $DragonFly: src/sys/vfs/gnu/ext2fs/Makefile,v 1.4 2006/04/04 17:34:32 dillon Exp $ .PATH: ${.CURDIR}/../../gnu/ext2fs KMOD= ext2fs SRCS= opt_ddb.h opt_quota.h opt_suiddir.h \ ext2_alloc.c ext2_balloc.c ext2_inode.c ext2_inode_cnv.c \ ext2_linux_balloc.c ext2_linux_ialloc.c ext2_lookup.c \ - ext2_subr.c ext2_vfsops.c ext2_vnops.c + ext2_subr.c ext2_vfsops.c ext2_vnops.c ext2_bmap.c \ + ext2_ihash.c ext2_quota.c .include diff --git a/sys/vfs/gnu/ext2fs/dinode.h b/sys/vfs/gnu/ext2fs/dinode.h new file mode 100644 index 0000000000..bfb9446173 --- /dev/null +++ b/sys/vfs/gnu/ext2fs/dinode.h @@ -0,0 +1,138 @@ +/*- + * Copyright (c) 1982, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. 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. + * + * @(#)dinode.h 8.3 (Berkeley) 1/21/94 + * $FreeBSD: src/sys/ufs/ufs/dinode.h,v 1.7 1999/08/28 00:52:27 peter Exp $ + * $DragonFly: src/sys/vfs/gnu/ext2fs/dinode.h,v 1.1 2006/04/04 17:34:32 dillon Exp $ + */ + +#ifndef _VFS_GNU_EXT2FS_DINODE_H_ +#define _VFS_GNU_EXT2FS_DINODE_H_ + +typedef __uint32_t ext2_ino_t; + +/* + * The root inode is the root of the filesystem. Inode 0 can't be used for + * normal purposes and historically bad blocks were linked to inode 1, thus + * the root inode is 2. (Inode 1 is no longer used for this purpose, however + * numerous dump tapes make this assumption, so we are stuck with it). + */ +#define ROOTINO ((ino_t)2) + +/* + * The Whiteout inode# is a dummy non-zero inode number which will + * never be allocated to a real file. It is used as a place holder + * in the directory entry which has been tagged as a DT_W entry. + * See the comments about ROOTINO above. + */ +#define WINO ((ino_t)1) + +/* + * A dinode contains all the meta-data associated with a EXT2 file. + * This structure defines the on-disk format of a dinode. Since + * this structure describes an on-disk structure, all its fields + * are defined by types with precise widths. + */ + +typedef __int32_t ext2_daddr_t; + +#define NDADDR 12 /* Direct addresses in inode. */ +#define NIADDR 3 /* Indirect addresses in inode. */ + +struct ext2_dinode { + uint16_t di_mode; /* 0: IFMT, permissions; see below. */ + int16_t di_nlink; /* 2: File link count. */ + union { + uint16_t oldids[2]; /* 4: Ffs: old user and group ids. */ + int32_t inumber; /* 4: Lfs: inode number. */ + } di_u; + uint64_t di_size; /* 8: File byte count. */ + int32_t di_atime; /* 16: Last access time. */ + int32_t di_atimensec; /* 20: Last access time. */ + int32_t di_mtime; /* 24: Last modified time. */ + int32_t di_mtimensec; /* 28: Last modified time. */ + int32_t di_ctime; /* 32: Last inode change time. */ + int32_t di_ctimensec; /* 36: Last inode change time. */ + ext2_daddr_t di_db[NDADDR]; /* 40: Direct disk blocks. */ + ext2_daddr_t di_ib[NIADDR]; /* 88: Indirect disk blocks. */ + uint32_t di_flags; /* 100: Status flags (chflags). */ + int32_t di_blocks; /* 104: Blocks actually held. */ + int32_t di_gen; /* 108: Generation number. */ + uint32_t di_uid; /* 112: File owner. */ + uint32_t di_gid; /* 116: File group. */ + union { /* 120: File hierarchy modified */ + int32_t spare[2]; /* (used by ext2fs) */ + int64_t fsmid; /* (used by dragonfly) */ + } di_v; +}; + +/* + * The di_db fields may be overlaid with other information for + * file types that do not have associated disk storage. Block + * and character devices overlay the first data block with their + * dev_t value. Short symbolic links place their path in the + * di_db area. + */ +#define di_inumber di_u.inumber +#define di_ogid di_u.oldids[1] +#define di_ouid di_u.oldids[0] +#define di_rdev di_db[0] +#define di_shortlink di_db +#define di_spare di_v.spare /* ext2fs */ +#define di_fsmid di_v.fsmid +#define MAXSYMLINKLEN ((NDADDR + NIADDR) * sizeof(ext2_daddr_t)) + +/* File permissions. */ +#define IEXEC 0000100 /* Executable. */ +#define IWRITE 0000200 /* Writeable. */ +#define IREAD 0000400 /* Readable. */ +#define ISVTX 0001000 /* Sticky bit. */ +#define ISGID 0002000 /* Set-gid. */ +#define ISUID 0004000 /* Set-uid. */ + +/* File types. */ +#define IFMT 0170000 /* Mask of file type. */ +#define IFIFO 0010000 /* Named pipe (fifo). */ +#define IFCHR 0020000 /* Character device. */ +#define IFDIR 0040000 /* Directory file. */ +#define IFBLK 0060000 /* Block device. */ +#define IFREG 0100000 /* Regular file. */ +#define IFLNK 0120000 /* Symbolic link. */ +#define IFSOCK 0140000 /* UNIX domain socket. */ +#define IFWHT 0160000 /* Whiteout. */ + +#endif /* !_VFS_GNU_EXT2FS_DINODE_H_ */ diff --git a/sys/vfs/gnu/ext2fs/dir.h b/sys/vfs/gnu/ext2fs/dir.h new file mode 100644 index 0000000000..09f952e523 --- /dev/null +++ b/sys/vfs/gnu/ext2fs/dir.h @@ -0,0 +1,160 @@ +/*- + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. 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. + * + * @(#)dir.h 8.2 (Berkeley) 1/21/94 + * $FreeBSD: src/sys/ufs/ufs/dir.h,v 1.9 1999/08/28 00:52:27 peter Exp $ + * $DragonFly: src/sys/vfs/gnu/ext2fs/dir.h,v 1.1 2006/04/04 17:34:32 dillon Exp $ + */ + +#ifndef _VFS_GNU_EXT2FS_DIR_H_ +#define _VFS_GNU_EXT2FS_DIR_H_ + +/* + * Theoretically, directories can be more than 2Gb in length, however, in + * practice this seems unlikely. So, we define the type doff_t as a 32-bit + * quantity to keep down the cost of doing lookup on a 32-bit machine. + */ +#define doff_t int32_t +#define MAXDIRSIZE (0x7fffffff) + +/* + * A directory consists of some number of blocks of DIRBLKSIZ + * bytes, where DIRBLKSIZ is chosen such that it can be transferred + * to disk in a single atomic operation (e.g. 512 bytes on most machines). + * + * Each DIRBLKSIZ byte block contains some number of directory entry + * structures, which are of variable length. Each directory entry has + * a struct direct at the front of it, containing its inode number, + * the length of the entry, and the length of the name contained in + * the entry. These are followed by the name padded to a 4 byte boundary + * with null bytes. All names are guaranteed null terminated. + * The maximum length of a name in a directory is MAXNAMLEN. + * + * The macro DIRSIZ(fmt, dp) gives the amount of space required to represent + * a directory entry. Free space in a directory is represented by + * entries which have dp->d_reclen > DIRSIZ(fmt, dp). All DIRBLKSIZ bytes + * in a directory block are claimed by the directory entries. This + * usually results in the last entry in a directory having a large + * dp->d_reclen. When entries are deleted from a directory, the + * space is returned to the previous entry in the same directory + * block by increasing its dp->d_reclen. If the first entry of + * a directory block is free, then its dp->d_ino is set to 0. + * Entries other than the first in a directory do not normally have + * dp->d_ino set to 0. + */ +#define DIRBLKSIZ DEV_BSIZE +#define MAXNAMLEN 255 + +struct direct { + uint32_t d_ino; /* inode number of entry */ + uint16_t d_reclen; /* length of this record */ + uint8_t d_type; /* file type, see below */ + uint8_t d_namlen; /* length of string in d_name */ + char d_name[MAXNAMLEN + 1];/* name with length <= MAXNAMLEN */ +}; + +/* + * File types + */ +#define DT_UNKNOWN 0 +#define DT_FIFO 1 +#define DT_CHR 2 +#define DT_DIR 4 +#define DT_BLK 6 +#define DT_REG 8 +#define DT_LNK 10 +#define DT_SOCK 12 +#define DT_WHT 14 + +/* + * Convert between stat structure types and directory types. + */ +#define IFTODT(mode) (((mode) & 0170000) >> 12) +#define DTTOIF(dirtype) ((dirtype) << 12) + +/* + * The DIRSIZ macro gives the minimum record length which will hold + * the directory entry. This requires the amount of space in struct direct + * without the d_name field, plus enough space for the name with a terminating + * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary. + * + * + */ +#define DIRECTSIZ(namlen) \ + (((int)&((struct direct *)0)->d_name + \ + ((namlen)+1)*sizeof(((struct direct *)0)->d_name[0]) + 3) & ~3) +#if (BYTE_ORDER == LITTLE_ENDIAN) +#define DIRSIZ(oldfmt, dp) \ + ((oldfmt) ? DIRECTSIZ((dp)->d_type) : DIRECTSIZ((dp)->d_namlen)) +#else +#define DIRSIZ(oldfmt, dp) \ + DIRECTSIZ((dp)->d_namlen) +#endif +#define OLDDIRFMT 1 +#define NEWDIRFMT 0 + +/* + * Template for manipulating directories. Should use struct direct's, + * but the name field is MAXNAMLEN - 1, and this just won't do. + */ +struct dirtemplate { + uint32_t dot_ino; + int16_t dot_reclen; + uint8_t dot_type; + uint8_t dot_namlen; + char dot_name[4]; /* must be multiple of 4 */ + uint32_t dotdot_ino; + int16_t dotdot_reclen; + uint8_t dotdot_type; + uint8_t dotdot_namlen; + char dotdot_name[4]; /* ditto */ +}; + +/* + * This is the old format of directories, sanz type element. + */ +struct odirtemplate { + uint32_t dot_ino; + int16_t dot_reclen; + uint16_t dot_namlen; + char dot_name[4]; /* must be multiple of 4 */ + uint32_t dotdot_ino; + int16_t dotdot_reclen; + uint16_t dotdot_namlen; + char dotdot_name[4]; /* ditto */ +}; +#endif /* _VFS_GNU_EXT2FS_DIR_H_ */ diff --git a/sys/vfs/gnu/ext2fs/ext2_alloc.c b/sys/vfs/gnu/ext2fs/ext2_alloc.c index e573fa09ab..614ec9ebb3 100644 --- a/sys/vfs/gnu/ext2fs/ext2_alloc.c +++ b/sys/vfs/gnu/ext2fs/ext2_alloc.c @@ -38,7 +38,7 @@ * * @(#)ext2_alloc.c 8.8 (Berkeley) 2/21/94 * $FreeBSD: src/sys/gnu/ext2fs/ext2_alloc.c,v 1.28.2.2 2002/07/01 00:18:51 iedowse Exp $ - * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2_alloc.c,v 1.10 2006/03/24 18:35:33 dillon Exp $ + * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2_alloc.c,v 1.11 2006/04/04 17:34:32 dillon Exp $ */ #include "opt_quota.h" @@ -53,9 +53,9 @@ #include -#include -#include -#include +#include "quota.h" +#include "inode.h" +#include "ext2mount.h" #include "ext2_fs.h" #include "ext2_fs_sb.h" @@ -126,7 +126,7 @@ ext2_alloc(struct inode *ip, daddr_t lbn, daddr_t bpref, int size, fs->s_es->s_free_blocks_count < fs->s_es->s_r_blocks_count) goto nospace; #if QUOTA - if ((error = chkdq(ip, (long)btodb(size), cred, 0)) != 0) + if ((error = ext2_chkdq(ip, (long)btodb(size), cred, 0)) != 0) return (error); #endif if (bpref >= fs->s_es->s_blocks_count) @@ -180,7 +180,7 @@ ext2_alloc(struct inode *ip, daddr_t lbn, daddr_t bpref, int size, /* * Restore user's disk quota because allocation failed. */ - chkdq(ip, (long)-btodb(size), cred, FORCE); + ext2_chkdq(ip, (long)-btodb(size), cred, FORCE); #endif nospace: ext2_fserr(fs, cred->cr_uid, "file system full"); @@ -257,8 +257,8 @@ return ENOSPC; if (dtog(fs, dofftofsb(fs, buflist->bs_children[0]->b_bio2.bio_offset)) != dtog(fs, dofftofsb(fs, buflist->bs_children[len - 1]->b_bio2.bio_offset))) return (ENOSPC); - if (ufs_getlbns(vp, start_lbn, start_ap, &start_lvl) || - ufs_getlbns(vp, end_lbn, end_ap, &end_lvl)) + if (ext2_getlbns(vp, start_lbn, start_ap, &start_lvl) || + ext2_getlbns(vp, end_lbn, end_ap, &end_lvl)) return (ENOSPC); /* * Get the starting offset and block map for the first block. @@ -339,7 +339,7 @@ return ENOSPC; } else { ip->i_flag |= IN_CHANGE | IN_UPDATE; if (!doasyncfree) - UFS_UPDATE(vp, 1); + EXT2_UPDATE(vp, 1); } if (ssize < len) if (doasyncfree) @@ -394,7 +394,7 @@ ext2_valloc(struct vnode *pvp, int mode, struct ucred *cred, struct vnode **vpp) goto noinodes; error = VFS_VGET(pvp->v_mount, ino, vpp); if (error) { - UFS_VFREE(pvp, ino, mode); + EXT2_VFREE(pvp, ino, mode); return (error); } ip = VTOI(*vpp); diff --git a/sys/vfs/gnu/ext2fs/ext2_balloc.c b/sys/vfs/gnu/ext2fs/ext2_balloc.c index abbb21c862..af3068888b 100644 --- a/sys/vfs/gnu/ext2fs/ext2_balloc.c +++ b/sys/vfs/gnu/ext2fs/ext2_balloc.c @@ -38,7 +38,7 @@ * * @(#)ffs_balloc.c 8.4 (Berkeley) 9/23/93 * $FreeBSD: src/sys/gnu/ext2fs/ext2_balloc.c,v 1.9.2.1 2000/08/03 00:52:57 peter Exp $ - * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2_balloc.c,v 1.8 2006/03/24 18:35:33 dillon Exp $ + * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2_balloc.c,v 1.9 2006/04/04 17:34:32 dillon Exp $ */ #include @@ -48,10 +48,8 @@ #include #include -#include -#include -#include - +#include "quota.h" +#include "inode.h" #include "ext2_fs.h" #include "ext2_fs_sb.h" #include "fs.h" @@ -159,11 +157,11 @@ ext2_debug("ext2_balloc called (%d, %d, %d)\n", * Determine the number of levels of indirection. */ pref = 0; - if ((error = ufs_getlbns(vp, bn, indirs, &num)) != 0) + if ((error = ext2_getlbns(vp, bn, indirs, &num)) != 0) return(error); #if DIAGNOSTIC if (num < 1) - panic ("ext2_balloc: ufs_bmaparray returned indirect block"); + panic ("ext2_balloc: ext2_bmaparray returned indirect block"); #endif /* * Fetch the first indirect block allocating if necessary. diff --git a/sys/vfs/gnu/ext2fs/ext2_bmap.c b/sys/vfs/gnu/ext2fs/ext2_bmap.c new file mode 100644 index 0000000000..e48bc98f2d --- /dev/null +++ b/sys/vfs/gnu/ext2fs/ext2_bmap.c @@ -0,0 +1,338 @@ +/* + * Copyright (c) 1989, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. 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. + * + * @(#)ufs_bmap.c 8.7 (Berkeley) 3/21/95 + * $FreeBSD: src/sys/ufs/ufs/ufs_bmap.c,v 1.34.2.1 2000/03/17 10:12:14 ps Exp $ + * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2_bmap.c,v 1.1 2006/04/04 17:34:32 dillon Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "quota.h" +#include "dinode.h" +#include "inode.h" +#include "ext2_fs_sb.h" +#include "ext2mount.h" +#include "ext2_extern.h" +#include "fs.h" + +static int ext2_bmaparray(struct vnode *vp, ext2_daddr_t bn, + ext2_daddr_t *bnp, struct indir *ap, int *nump, + int *runp, int *runb); + +/* + * Bmap converts the logical block number of a file to its physical block + * number on the disk. The conversion is done by using the logical block + * number to index into the array of block pointers described by the dinode. + * + * BMAP must return the contiguous before and after run in bytes, inclusive + * of the returned block. + * + * ext2_bmap(struct vnode *a_vp, off_t a_loffset, struct vnode **a_vpp, + * off_t *a_doffsetp, int *a_runp, int *a_runb) + */ +int +ext2_bmap(struct vop_bmap_args *ap) +{ + struct ext2_sb_info *fs; + ext2_daddr_t lbn; + ext2_daddr_t dbn; + int error; + + /* + * Check for underlying vnode requests and ensure that logical + * to physical mapping is requested. + */ + if (ap->a_vpp != NULL) + *ap->a_vpp = VTOI(ap->a_vp)->i_devvp; + if (ap->a_doffsetp == NULL) + return (0); + + fs = VTOI(ap->a_vp)->i_e2fs; + KKASSERT(((int)ap->a_loffset & ((1 << fs->s_bshift) - 1)) == 0); + lbn = ap->a_loffset >> fs->s_bshift; + + error = ext2_bmaparray(ap->a_vp, lbn, &dbn, NULL, NULL, + ap->a_runp, ap->a_runb); + + if (error || dbn == (ext2_daddr_t)-1) { + *ap->a_doffsetp = NOOFFSET; + } else { + *ap->a_doffsetp = dbtodoff(fs, dbn); + if (ap->a_runp) + *ap->a_runp = (*ap->a_runp + 1) << fs->s_bshift; + if (ap->a_runb) + *ap->a_runb = *ap->a_runb << fs->s_bshift; + } + return (error); +} + +/* + * Indirect blocks are now on the vnode for the file. They are given negative + * logical block numbers. Indirect blocks are addressed by the negative + * address of the first data block to which they point. Double indirect blocks + * are addressed by one less than the address of the first indirect block to + * which they point. Triple indirect blocks are addressed by one less than + * the address of the first double indirect block to which they point. + * + * ext2_bmaparray does the bmap conversion, and if requested returns the + * array of logical blocks which must be traversed to get to a block. + * Each entry contains the offset into that block that gets you to the + * next block and the disk address of the block (if it is assigned). + */ +static +int +ext2_bmaparray(struct vnode *vp, ext2_daddr_t bn, ext2_daddr_t *bnp, + struct indir *ap, int *nump, int *runp, int *runb) +{ + struct inode *ip; + struct buf *bp; + struct ext2mount *ump; + struct mount *mp; + struct vnode *devvp; + struct ext2_sb_info *fs; + struct indir a[NIADDR+1], *xap; + ext2_daddr_t daddr; + long metalbn; + int error, maxrun, num; + + ip = VTOI(vp); + mp = vp->v_mount; + ump = VFSTOEXT2(mp); + devvp = ump->um_devvp; + fs = ip->i_e2fs; +#ifdef DIAGNOSTIC + if ((ap != NULL && nump == NULL) || (ap == NULL && nump != NULL)) + panic("ext2_bmaparray: invalid arguments"); +#endif + + if (runp) { + *runp = 0; + } + + if (runb) { + *runb = 0; + } + + maxrun = mp->mnt_iosize_max / mp->mnt_stat.f_iosize - 1; + + xap = ap == NULL ? a : ap; + if (!nump) + nump = # + error = ext2_getlbns(vp, bn, xap, nump); + if (error) + return (error); + + num = *nump; + if (num == 0) { + *bnp = blkptrtodb(ump, ip->i_db[bn]); + if (*bnp == 0) + *bnp = -1; + else if (runp) { + daddr_t bnb = bn; + for (++bn; bn < NDADDR && *runp < maxrun && + is_sequential(ump, ip->i_db[bn - 1], ip->i_db[bn]); + ++bn, ++*runp); + bn = bnb; + if (runb && (bn > 0)) { + for (--bn; (bn >= 0) && (*runb < maxrun) && + is_sequential(ump, ip->i_db[bn], + ip->i_db[bn+1]); + --bn, ++*runb); + } + } + return (0); + } + + + /* Get disk address out of indirect block array */ + daddr = ip->i_ib[xap->in_off]; + + for (bp = NULL, ++xap; --num; ++xap) { + /* + * Exit the loop if there is no disk address assigned yet and + * the indirect block isn't in the cache, or if we were + * looking for an indirect block and we've found it. + */ + + metalbn = xap->in_lbn; + if ((daddr == 0 && !findblk(vp, dbtodoff(fs, metalbn))) || metalbn == bn) + break; + /* + * If we get here, we've either got the block in the cache + * or we have a disk address for it, go fetch it. + */ + if (bp) + bqrelse(bp); + + xap->in_exists = 1; + bp = getblk(vp, lblktodoff(fs, metalbn), + mp->mnt_stat.f_iosize, 0, 0); + if ((bp->b_flags & B_CACHE) == 0) { +#ifdef DIAGNOSTIC + if (!daddr) + panic("ext2_bmaparray: indirect block not in cache"); +#endif + bp->b_bio2.bio_offset = fsbtodoff(fs, daddr); + bp->b_flags |= B_READ; + bp->b_flags &= ~(B_INVAL|B_ERROR); + vfs_busy_pages(bp, 0); + vn_strategy(bp->b_vp, &bp->b_bio1); + error = biowait(bp); + if (error) { + brelse(bp); + return (error); + } + } + + daddr = ((ext2_daddr_t *)bp->b_data)[xap->in_off]; + if (num == 1 && daddr && runp) { + for (bn = xap->in_off + 1; + bn < MNINDIR(ump) && *runp < maxrun && + is_sequential(ump, + ((ext2_daddr_t *)bp->b_data)[bn - 1], + ((ext2_daddr_t *)bp->b_data)[bn]); + ++bn, ++*runp); + bn = xap->in_off; + if (runb && bn) { + for(--bn; bn >= 0 && *runb < maxrun && + is_sequential(ump, ((daddr_t *)bp->b_data)[bn], + ((daddr_t *)bp->b_data)[bn+1]); + --bn, ++*runb); + } + } + } + if (bp) + bqrelse(bp); + + daddr = blkptrtodb(ump, daddr); + *bnp = daddr == 0 ? -1 : daddr; + return (0); +} + +/* + * Create an array of logical block number/offset pairs which represent the + * path of indirect blocks required to access a data block. The first "pair" + * contains the logical block number of the appropriate single, double or + * triple indirect block and the offset into the inode indirect block array. + * Note, the logical block number of the inode single/double/triple indirect + * block appears twice in the array, once with the offset into the i_ib and + * once with the offset into the page itself. + */ +int +ext2_getlbns(struct vnode *vp, ext2_daddr_t bn, struct indir *ap, int *nump) +{ + long blockcnt, metalbn, realbn; + struct ext2mount *ump; + int i, numlevels, off; + int64_t qblockcnt; + + ump = VFSTOEXT2(vp->v_mount); + if (nump) + *nump = 0; + numlevels = 0; + realbn = bn; + if ((long)bn < 0) + bn = -(long)bn; + + /* The first NDADDR blocks are direct blocks. */ + if (bn < NDADDR) + return (0); + + /* + * Determine the number of levels of indirection. After this loop + * is done, blockcnt indicates the number of data blocks possible + * at the previous level of indirection, and NIADDR - i is the number + * of levels of indirection needed to locate the requested block. + */ + for (blockcnt = 1, i = NIADDR, bn -= NDADDR;; i--, bn -= blockcnt) { + if (i == 0) + return (EFBIG); + /* + * Use int64_t's here to avoid overflow for triple indirect + * blocks when longs have 32 bits and the block size is more + * than 4K. + */ + qblockcnt = (int64_t)blockcnt * MNINDIR(ump); + if (bn < qblockcnt) + break; + blockcnt = qblockcnt; + } + + /* Calculate the address of the first meta-block. */ + if (realbn >= 0) + metalbn = -(realbn - bn + NIADDR - i); + else + metalbn = -(-realbn - bn + NIADDR - i); + + /* + * At each iteration, off is the offset into the bap array which is + * an array of disk addresses at the current level of indirection. + * The logical block number and the offset in that block are stored + * into the argument array. + */ + ap->in_lbn = metalbn; + ap->in_off = off = NIADDR - i; + ap->in_exists = 0; + ap++; + for (++numlevels; i <= NIADDR; i++) { + /* If searching for a meta-data block, quit when found. */ + if (metalbn == realbn) + break; + + off = (bn / blockcnt) % MNINDIR(ump); + + ++numlevels; + ap->in_lbn = metalbn; + ap->in_off = off; + ap->in_exists = 0; + ++ap; + + metalbn -= -1 + off * blockcnt; + blockcnt /= MNINDIR(ump); + } + if (nump) + *nump = numlevels; + return (0); +} diff --git a/sys/vfs/gnu/ext2fs/ext2_extern.h b/sys/vfs/gnu/ext2fs/ext2_extern.h index 88680884c2..15a9757017 100644 --- a/sys/vfs/gnu/ext2fs/ext2_extern.h +++ b/sys/vfs/gnu/ext2fs/ext2_extern.h @@ -38,18 +38,19 @@ * * @(#)ffs_extern.h 8.3 (Berkeley) 4/16/94 * $FreeBSD: src/sys/gnu/ext2fs/ext2_extern.h,v 1.22.6.1 2000/11/05 19:17:40 bde Exp $ - * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2_extern.h,v 1.9 2005/09/14 01:13:35 dillon Exp $ + * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2_extern.h,v 1.10 2006/04/04 17:34:32 dillon Exp $ */ -#ifndef _SYS_GNU_EXT2FS_EXT2_EXTERN_H_ -#define _SYS_GNU_EXT2FS_EXT2_EXTERN_H_ +#ifndef _VFS_GNU_EXT2FS_EXT2_EXTERN_H_ +#define _VFS_GNU_EXT2FS_EXT2_EXTERN_H_ -struct dinode; +struct ext2_dinode; struct ext2_inode; struct inode; struct mount; struct vfsconf; struct vnode; +struct indir; int ext2_alloc (struct inode *, daddr_t, daddr_t, int, struct ucred *, daddr_t *); @@ -68,7 +69,7 @@ int ext2_valloc (struct vnode *, int, struct ucred *, struct vnode **); int ext2_vfree (struct vnode *, ino_t, int); int ext2_lookup (struct vop_old_lookup_args *); int ext2_readdir (struct vop_readdir_args *); -void ext2_print_dinode (struct dinode *); +void ext2_print_dinode (struct ext2_dinode *); void ext2_print_inode (struct inode *); int ext2_direnter (struct inode *, struct vnode *, struct componentname *); @@ -90,14 +91,31 @@ unsigned long ext2_count_free (struct buf *map, unsigned int numchars); void ext2_free_blocks (struct mount * mp, unsigned long block, unsigned long count); void ext2_free_inode (struct inode * inode); -void ext2_ei2di (struct ext2_inode *ei, struct dinode *di); -void ext2_di2ei (struct dinode *di, struct ext2_inode *ei); +void ext2_ei2di (struct ext2_inode *ei, struct ext2_dinode *di); +void ext2_di2ei (struct ext2_dinode *di, struct ext2_inode *ei); void mark_buffer_dirty (struct buf *bh); +int ext2_getlbns(struct vnode *, ext2_daddr_t, struct indir *, int *); +void ext2_itimes(struct vnode *vp); +struct vnode *ext2_ihashget(dev_t, ino_t); +int ext2_ihashins(struct inode *); +void ext2_ihashrem(struct inode *); +void ext2_dirbad(struct inode *, doff_t, char *); +int ext2_check_export(struct mount *, struct sockaddr *, int *, + struct ucred **); +int ext2_vinit(struct mount *, struct vnode **); +int ext2_vnoperate(struct vop_generic_args *); +int ext2_vnoperatefifo(struct vop_generic_args *); +int ext2_vnoperatespec(struct vop_generic_args *); +int ext2_uninit(struct vfsconf *); +void ext2_ihashinit(void); +struct vnode *ext2_ihashlookup(dev_t dev, ino_t inum); +int ext2_ihashcheck(dev_t dev, ino_t inum); + /* * This macro allows the ufs code to distinguish between an EXT2 and a * non-ext2(FFS/LFS) vnode. */ #define IS_EXT2_VNODE(vp) (vp->v_mount->mnt_stat.f_type == MOUNT_EXT2FS) -#endif /* !_SYS_GNU_EXT2FS_EXT2_EXTERN_H_ */ +#endif /* !_VFS_GNU_EXT2FS_EXT2_EXTERN_H_ */ diff --git a/sys/vfs/gnu/ext2fs/ext2_ihash.c b/sys/vfs/gnu/ext2fs/ext2_ihash.c new file mode 100644 index 0000000000..0fde52a419 --- /dev/null +++ b/sys/vfs/gnu/ext2fs/ext2_ihash.c @@ -0,0 +1,231 @@ +/* + * Copyright (c) 1982, 1986, 1989, 1991, 1993, 1995 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. 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. + * + * @(#)ufs_ihash.c 8.7 (Berkeley) 5/17/95 + * $FreeBSD: src/sys/ufs/ufs/ufs_ihash.c,v 1.20 1999/08/28 00:52:29 peter Exp $ + * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2_ihash.c,v 1.1 2006/04/04 17:34:32 dillon Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "quota.h" +#include "dinode.h" +#include "inode.h" +#include "ext2_extern.h" + +static MALLOC_DEFINE(M_EXT2IHASH, "EXT2 ihash", "EXT2 Inode hash tables"); +/* + * Structures associated with inode cacheing. + */ +static struct inode **ext2_ihashtbl; +static u_long ext2_ihash; /* size of hash table - 1 */ +static struct lwkt_token ext2_ihash_token; + +#define INOHASH(device, inum) (&ext2_ihashtbl[(minor(device) + (inum)) & ext2_ihash]) + +/* + * Initialize inode hash table. + */ +void +ext2_ihashinit(void) +{ + ext2_ihash = 16; + while (ext2_ihash < desiredvnodes) + ext2_ihash <<= 1; + ext2_ihashtbl = malloc(sizeof(void *) * ext2_ihash, M_EXT2IHASH, M_WAITOK|M_ZERO); + --ext2_ihash; + lwkt_token_init(&ext2_ihash_token); +} + +int +ext2_uninit(struct vfsconf *vfc) +{ + lwkt_tokref ilock; + + lwkt_gettoken(&ilock, &ext2_ihash_token); + if (ext2_ihashtbl) + free(ext2_ihashtbl, M_EXT2IHASH); + lwkt_reltoken(&ilock); + + return (0); +} +/* + * Use the device/inum pair to find the incore inode, and return a pointer + * to it. If it is in core, return it, even if it is locked. + */ +struct vnode * +ext2_ihashlookup(dev_t dev, ino_t inum) +{ + struct inode *ip; + lwkt_tokref ilock; + + lwkt_gettoken(&ilock, &ext2_ihash_token); + for (ip = *INOHASH(dev, inum); ip; ip = ip->i_next) { + if (inum == ip->i_number && dev == ip->i_dev) + break; + } + lwkt_reltoken(&ilock); + if (ip) + return (ITOV(ip)); + return (NULLVP); +} + +/* + * Use the device/inum pair to find the incore inode, and return a pointer + * to it. If it is in core, but locked, wait for it. + * + * Note that the serializing tokens do not prevent other processes from + * playing with the data structure being protected while we are blocked. + * They do protect us from preemptive interrupts which might try to + * play with the protected data structure. + */ +struct vnode * +ext2_ihashget(dev_t dev, ino_t inum) +{ + struct thread *td = curthread; /* XXX */ + lwkt_tokref ilock; + struct inode *ip; + struct vnode *vp; + + lwkt_gettoken(&ilock, &ext2_ihash_token); +loop: + for (ip = *INOHASH(dev, inum); ip; ip = ip->i_next) { + if (inum != ip->i_number || dev != ip->i_dev) + continue; + vp = ITOV(ip); + if (vget(vp, LK_EXCLUSIVE, td)) + goto loop; + /* + * We must check to see if the inode has been ripped + * out from under us after blocking. + */ + for (ip = *INOHASH(dev, inum); ip; ip = ip->i_next) { + if (inum == ip->i_number && dev == ip->i_dev) + break; + } + if (ip == NULL || ITOV(ip) != vp) { + vput(vp); + goto loop; + } + lwkt_reltoken(&ilock); + return (vp); + } + lwkt_reltoken(&ilock); + return (NULL); +} + +/* + * Check to see if an inode is in the hash table. This is used to interlock + * file free operations to ensure that the vnode is not reused due to a + * reallocate of its inode number before we have had a chance to recycle it. + */ +int +ext2_ihashcheck(dev_t dev, ino_t inum) +{ + lwkt_tokref ilock; + struct inode *ip; + + lwkt_gettoken(&ilock, &ext2_ihash_token); + for (ip = *INOHASH(dev, inum); ip; ip = ip->i_next) { + if (inum == ip->i_number && dev == ip->i_dev) { + if (ip->i_vnode) { + printf("conflict with vnode %p", ip->i_vnode); + if (TAILQ_FIRST(&ip->i_vnode->v_namecache)) + printf(" ncp %s", TAILQ_FIRST(&ip->i_vnode->v_namecache)->nc_name); + printf("\n"); + } + break; + } + } + lwkt_reltoken(&ilock); + return(ip ? 1 : 0); +} + +/* + * Insert the inode into the hash table, and return it locked. + */ +int +ext2_ihashins(struct inode *ip) +{ + struct inode **ipp; + struct inode *iq; + lwkt_tokref ilock; + + KKASSERT((ip->i_flag & IN_HASHED) == 0); + lwkt_gettoken(&ilock, &ext2_ihash_token); + ipp = INOHASH(ip->i_dev, ip->i_number); + while ((iq = *ipp) != NULL) { + if (ip->i_dev == iq->i_dev && ip->i_number == iq->i_number) { + lwkt_reltoken(&ilock); + return(EBUSY); + } + ipp = &iq->i_next; + } + ip->i_next = NULL; + *ipp = ip; + ip->i_flag |= IN_HASHED; + lwkt_reltoken(&ilock); + return(0); +} + +/* + * Remove the inode from the hash table. + */ +void +ext2_ihashrem(struct inode *ip) +{ + lwkt_tokref ilock; + struct inode **ipp; + struct inode *iq; + + lwkt_gettoken(&ilock, &ext2_ihash_token); + if (ip->i_flag & IN_HASHED) { + ipp = INOHASH(ip->i_dev, ip->i_number); + while ((iq = *ipp) != NULL) { + if (ip == iq) + break; + ipp = &iq->i_next; + } + KKASSERT(ip == iq); + *ipp = ip->i_next; + ip->i_next = NULL; + ip->i_flag &= ~IN_HASHED; + } + lwkt_reltoken(&ilock); +} + diff --git a/sys/vfs/gnu/ext2fs/ext2_inode.c b/sys/vfs/gnu/ext2fs/ext2_inode.c index 11d6605437..86c90cff45 100644 --- a/sys/vfs/gnu/ext2fs/ext2_inode.c +++ b/sys/vfs/gnu/ext2fs/ext2_inode.c @@ -38,7 +38,7 @@ * * @(#)ext2_inode.c 8.5 (Berkeley) 12/30/93 * $FreeBSD: src/sys/gnu/ext2fs/ext2_inode.c,v 1.24.2.1 2000/08/03 00:52:57 peter Exp $ - * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2_inode.c,v 1.12 2006/03/24 18:35:33 dillon Exp $ + * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2_inode.c,v 1.13 2006/04/04 17:34:32 dillon Exp $ */ #include "opt_quota.h" @@ -53,10 +53,9 @@ #include #include -#include -#include -#include -#include +#include "quota.h" +#include "inode.h" +#include "ext2mount.h" #include "ext2_fs.h" #include "ext2_fs_sb.h" @@ -83,7 +82,7 @@ ext2_update(struct vnode *vp, int waitfor) struct inode *ip; int error; - ufs_itimes(vp); + ext2_itimes(vp); ip = VTOI(vp); if ((ip->i_flag & IN_MODIFIED) == 0) return (0); @@ -154,14 +153,14 @@ printf("ext2_truncate called %d to %d\n", VTOI(ovp)->i_number, length); bzero((char *)&oip->i_shortlink, (u_int)oip->i_size); oip->i_size = 0; oip->i_flag |= IN_CHANGE | IN_UPDATE; - return (UFS_UPDATE(ovp, 1)); + return (EXT2_UPDATE(ovp, 1)); } if (oip->i_size == length) { oip->i_flag |= IN_CHANGE | IN_UPDATE; - return (UFS_UPDATE(ovp, 0)); + return (EXT2_UPDATE(ovp, 0)); } #if QUOTA - if ((error = getinoquota(oip)) != 0) + if ((error = ext2_getinoquota(oip)) != 0) return (error); #endif fs = oip->i_e2fs; @@ -188,7 +187,7 @@ printf("ext2_truncate called %d to %d\n", VTOI(ovp)->i_number, length); else bawrite(bp); oip->i_flag |= IN_CHANGE | IN_UPDATE; - return (UFS_UPDATE(ovp, 1)); + return (EXT2_UPDATE(ovp, 1)); } /* * Shorten the size of the file. If the file is not being @@ -244,7 +243,7 @@ printf("ext2_truncate called %d to %d\n", VTOI(ovp)->i_number, length); for (i = NDADDR - 1; i > lastblock; i--) oip->i_db[i] = 0; oip->i_flag |= IN_CHANGE | IN_UPDATE; - allerror = UFS_UPDATE(ovp, 1); + allerror = EXT2_UPDATE(ovp, 1); /* * Having written the new inode to disk, save its new configuration @@ -350,7 +349,7 @@ done: oip->i_flag |= IN_CHANGE; vnode_pager_setsize(ovp, length); #if QUOTA - chkdq(oip, -blocksreleased, NOCRED, 0); + ext2_chkdq(oip, -blocksreleased, NOCRED, 0); #endif return (allerror); } @@ -468,13 +467,113 @@ ext2_indirtrunc(struct inode *ip, daddr_t lbn, off_t doffset, daddr_t lastbn, } /* - * discard preallocated blocks + * Last reference to an inode. If necessary, write or delete it. * - * ext2_inactive(struct vnode *a_vp) + * ext2_inactive(struct vnode *a_vp, struct thread *a_td) */ int ext2_inactive(struct vop_inactive_args *ap) { - ext2_discard_prealloc(VTOI(ap->a_vp)); - return ufs_inactive(ap); + struct vnode *vp = ap->a_vp; + struct inode *ip = VTOI(vp); + struct thread *td = ap->a_td; + int mode, error = 0; + + ext2_discard_prealloc(ip); + if (prtactive && vp->v_usecount != 1) + vprint("ext2_inactive: pushing active", vp); + + /* + * Ignore inodes related to stale file handles. + */ + if (ip == NULL || ip->i_mode == 0) + goto out; + if (ip->i_nlink <= 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { +#ifdef QUOTA + if (!ext2_getinoquota(ip)) + (void)ext2_chkiq(ip, -1, NOCRED, FORCE); +#endif + error = EXT2_TRUNCATE(vp, (off_t)0, 0, NOCRED, td); + ip->i_rdev = 0; + mode = ip->i_mode; + ip->i_mode = 0; + ip->i_flag |= IN_CHANGE | IN_UPDATE; + EXT2_VFREE(vp, ip->i_number, mode); + } + if (ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) + EXT2_UPDATE(vp, 0); +out: + /* + * If we are done with the inode, reclaim it + * so that it can be reused immediately. + */ + if (ip == NULL || ip->i_mode == 0) + vrecycle(vp, td); + return (error); +} + +/* + * Reclaim an inode so that it can be used for other purposes. + * + * ext2_reclaim(struct vnode *a_vp, struct thread *a_td) + */ +int +ext2_reclaim(struct vop_reclaim_args *ap) +{ + struct inode *ip; + struct vnode *vp = ap->a_vp; +#ifdef QUOTA + int i; +#endif + + if (prtactive && vp->v_usecount != 1) + vprint("ext2_reclaim: pushing active", vp); + ip = VTOI(vp); + + /* + * Lazy updates. + */ + if (ip) { + if (ap->a_retflags & NCF_FSMID) { + ++ip->i_fsmid; + ip->i_flag |= IN_LAZYMOD; + } + if (ip->i_flag & IN_LAZYMOD) { + ip->i_flag |= IN_MODIFIED; + EXT2_UPDATE(vp, 0); + } + } +#ifdef INVARIANTS + if (ip && (ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE))) { + printf("WARNING: INODE %ld flags %08x: modified inode being released!\n", (long)ip->i_number, (int)ip->i_flag); + ip->i_flag |= IN_MODIFIED; + EXT2_UPDATE(vp, 0); + } +#endif + /* + * Remove the inode from its hash chain and purge namecache + * data associated with the vnode. + */ + vp->v_data = NULL; + if (ip) { + ext2_ihashrem(ip); + if (ip->i_devvp) { + vrele(ip->i_devvp); + ip->i_devvp = 0; + } +#ifdef QUOTA + for (i = 0; i < MAXQUOTAS; i++) { + if (ip->i_dquot[i] != NODQUOT) { + ext2_dqrele(vp, ip->i_dquot[i]); + ip->i_dquot[i] = NODQUOT; + } + } +#endif +#ifdef UFS_DIRHASH + if (ip->i_dirhash != NULL) + ext2dirhash_free(ip); +#endif + free(ip, VFSTOEXT2(vp->v_mount)->um_malloctype); + } + return (0); } diff --git a/sys/vfs/gnu/ext2fs/ext2_inode_cnv.c b/sys/vfs/gnu/ext2fs/ext2_inode_cnv.c index 881c6ce723..1d57ab33c8 100644 --- a/sys/vfs/gnu/ext2fs/ext2_inode_cnv.c +++ b/sys/vfs/gnu/ext2fs/ext2_inode_cnv.c @@ -20,7 +20,7 @@ * * Utah $Hdr$ * $FreeBSD: src/sys/gnu/ext2fs/ext2_inode_cnv.c,v 1.11 2000/01/01 17:39:21 bde Exp $ - * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2_inode_cnv.c,v 1.5 2005/08/02 13:03:55 joerg Exp $ + * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2_inode_cnv.c,v 1.6 2006/04/04 17:34:32 dillon Exp $ */ /* @@ -34,34 +34,15 @@ #include -#include -#include - -/* - * Undo the definitions in that would destroy the include - * of . - */ -#undef i_atime -#undef i_blocks -#undef i_ctime -#undef i_db -#undef i_flags -#undef i_gen -#undef i_gid -#undef i_ib -#undef i_mode -#undef i_mtime -#undef i_nlink -#undef i_rdev -#undef i_shortlink -#undef i_size -#undef i_uid +#include "quota.h" +#define NO_I_DEFINES +#include "inode.h" #include "ext2_fs.h" #include "ext2_extern.h" void -ext2_print_dinode(struct dinode *di) +ext2_print_dinode(struct ext2_dinode *di) { int i; printf( /* "Inode: %5d" */ @@ -92,7 +73,7 @@ ext2_print_inode(struct inode *in) * raw ext2 inode to dinode */ void -ext2_ei2di(struct ext2_inode *ei, struct dinode *di) +ext2_ei2di(struct ext2_inode *ei, struct ext2_dinode *di) { int i; @@ -125,7 +106,7 @@ ext2_ei2di(struct ext2_inode *ei, struct dinode *di) * dinode to raw ext2 inode */ void -ext2_di2ei(struct dinode *di, struct ext2_inode *ei) +ext2_di2ei(struct ext2_dinode *di, struct ext2_inode *ei) { int i; diff --git a/sys/vfs/gnu/ext2fs/ext2_linux_balloc.c b/sys/vfs/gnu/ext2fs/ext2_linux_balloc.c index e1063b90eb..0bdf687e5d 100644 --- a/sys/vfs/gnu/ext2fs/ext2_linux_balloc.c +++ b/sys/vfs/gnu/ext2fs/ext2_linux_balloc.c @@ -5,7 +5,7 @@ * University of Utah, Department of Computer Science * * $FreeBSD: src/sys/gnu/ext2fs/ext2_linux_balloc.c,v 1.11.2.3 2001/08/14 18:03:19 gallatin Exp $ - * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2_linux_balloc.c,v 1.7 2006/03/24 18:35:33 dillon Exp $ + * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2_linux_balloc.c,v 1.8 2006/04/04 17:34:32 dillon Exp $ */ /* * linux/fs/ext2/balloc.c @@ -38,8 +38,10 @@ #include #include -#include -#include +#include "quota.h" +#include "dinode.h" +#include "inode.h" +#include "ext2mount.h" #include "ext2_extern.h" #include "ext2_fs.h" #include "ext2_fs_sb.h" @@ -63,13 +65,13 @@ static void read_block_bitmap(struct mount *mp, unsigned int block_group, unsigned long bitmap_nr) { - struct ext2_sb_info *sb = VFSTOUFS(mp)->um_e2fs; + struct ext2_sb_info *sb = VFSTOEXT2(mp)->um_e2fs; struct ext2_group_desc * gdp; struct buffer_head * bh; int error; gdp = get_group_desc (mp, block_group, NULL); - error = bread(VFSTOUFS(mp)->um_devvp, + error = bread(VFSTOEXT2(mp)->um_devvp, fsbtodoff(sb, gdp->bg_block_bitmap), sb->s_blocksize, &bh); if (error) { @@ -98,7 +100,7 @@ static int load__block_bitmap(struct mount *mp, unsigned int block_group) { int i, j; - struct ext2_sb_info *sb = VFSTOUFS(mp)->um_e2fs; + struct ext2_sb_info *sb = VFSTOEXT2(mp)->um_e2fs; unsigned long block_bitmap_number; struct buffer_head * block_bitmap; @@ -157,7 +159,7 @@ load__block_bitmap(struct mount *mp, unsigned int block_group) static __inline int load_block_bitmap(struct mount * mp, unsigned int block_group) { - struct ext2_sb_info *sb = VFSTOUFS(mp)->um_e2fs; + struct ext2_sb_info *sb = VFSTOEXT2(mp)->um_e2fs; if (sb->s_loaded_block_bitmaps > 0 && sb->s_block_bitmap_number[0] == block_group) return 0; @@ -174,7 +176,7 @@ void ext2_free_blocks(struct mount * mp, unsigned long block, unsigned long count) { - struct ext2_sb_info *sb = VFSTOUFS(mp)->um_e2fs; + struct ext2_sb_info *sb = VFSTOEXT2(mp)->um_e2fs; struct buffer_head * bh; struct buffer_head * bh2; unsigned long block_group; @@ -188,13 +190,13 @@ ext2_free_blocks(struct mount * mp, unsigned long block, printf ("ext2_free_blocks: nonexistent device"); return; } - lock_super (VFSTOUFS(mp)->um_devvp); + lock_super (VFSTOEXT2(mp)->um_devvp); if (block < es->s_first_data_block || (block + count) > es->s_blocks_count) { printf ( "ext2_free_blocks: " "Freeing blocks not in datazone - " "block = %lu, count = %lu", block, count); - unlock_super (VFSTOUFS(mp)->um_devvp); + unlock_super (VFSTOEXT2(mp)->um_devvp); return; } @@ -244,7 +246,7 @@ ext2_free_blocks(struct mount * mp, unsigned long block, } ****/ sb->s_dirt = 1; - unlock_super (VFSTOUFS(mp)->um_devvp); + unlock_super (VFSTOEXT2(mp)->um_devvp); return; } @@ -260,7 +262,7 @@ ext2_new_block(struct mount * mp, unsigned long goal, u_int32_t * prealloc_count, u_int32_t * prealloc_block) { - struct ext2_sb_info *sb = VFSTOUFS(mp)->um_e2fs; + struct ext2_sb_info *sb = VFSTOEXT2(mp)->um_e2fs; struct buffer_head * bh; struct buffer_head * bh2; char * p, * r; @@ -276,7 +278,7 @@ ext2_new_block(struct mount * mp, unsigned long goal, printf ("ext2_new_block: nonexistent device"); return 0; } - lock_super (VFSTOUFS(mp)->um_devvp); + lock_super (VFSTOEXT2(mp)->um_devvp); ext2_debug ("goal=%lu.\n", goal); @@ -363,7 +365,7 @@ repeat: break; } if (k >= sb->s_groups_count) { - unlock_super (VFSTOUFS(mp)->um_devvp); + unlock_super (VFSTOEXT2(mp)->um_devvp); return 0; } bitmap_nr = load_block_bitmap (mp, i); @@ -379,7 +381,7 @@ repeat: if (j >= EXT2_BLOCKS_PER_GROUP(sb)) { printf ( "ext2_new_block: " "Free blocks count corrupted for block group %d", i); - unlock_super (VFSTOUFS(mp)->um_devvp); + unlock_super (VFSTOEXT2(mp)->um_devvp); return 0; } @@ -446,7 +448,7 @@ got_block: printf ( "ext2_new_block: " "block >= blocks count - " "block_group = %d, block=%d", i, j); - unlock_super (VFSTOUFS(mp)->um_devvp); + unlock_super (VFSTOEXT2(mp)->um_devvp); return 0; } @@ -457,7 +459,7 @@ got_block: mark_buffer_dirty(bh2); es->s_free_blocks_count--; sb->s_dirt = 1; - unlock_super (VFSTOUFS(mp)->um_devvp); + unlock_super (VFSTOEXT2(mp)->um_devvp); return j; } @@ -465,7 +467,7 @@ got_block: static unsigned long ext2_count_free_blocks(struct mount * mp) { - struct ext2_sb_info *sb = VFSTOUFS(mp)->um_e2fs; + struct ext2_sb_info *sb = VFSTOEXT2(mp)->um_e2fs; #ifdef EXT2FS_DEBUG struct ext2_super_block * es; unsigned long desc_count, bitmap_count, x; @@ -473,7 +475,7 @@ ext2_count_free_blocks(struct mount * mp) struct ext2_group_desc * gdp; int i; - lock_super (VFSTOUFS(mp)->um_devvp); + lock_super (VFSTOEXT2(mp)->um_devvp); es = sb->s_es; desc_count = 0; bitmap_count = 0; @@ -490,7 +492,7 @@ ext2_count_free_blocks(struct mount * mp) } ext2_debug( "stored = %lu, computed = %lu, %lu\n", es->s_free_blocks_count, desc_count, bitmap_count); - unlock_super (VFSTOUFS(mp)->um_devvp); + unlock_super (VFSTOEXT2(mp)->um_devvp); return bitmap_count; #else return sb->s_es->s_free_blocks_count; @@ -531,7 +533,7 @@ ext2_group_sparse(int group) static void ext2_check_blocks_bitmap(struct mount * mp) { - struct ext2_sb_info *sb = VFSTOUFS(mp)->um_e2fs; + struct ext2_sb_info *sb = VFSTOEXT2(mp)->um_e2fs; struct buffer_head * bh; struct ext2_super_block * es; unsigned long desc_count, bitmap_count, x; @@ -540,7 +542,7 @@ ext2_check_blocks_bitmap(struct mount * mp) struct ext2_group_desc * gdp; int i, j; - lock_super (VFSTOUFS(mp)->um_devvp); + lock_super (VFSTOEXT2(mp)->um_devvp); es = sb->s_es; desc_count = 0; bitmap_count = 0; @@ -597,7 +599,7 @@ ext2_check_blocks_bitmap(struct mount * mp) "Wrong free blocks count in super block, " "stored = %lu, counted = %lu", (unsigned long) es->s_free_blocks_count, bitmap_count); - unlock_super (VFSTOUFS(mp)->um_devvp); + unlock_super (VFSTOEXT2(mp)->um_devvp); } #endif /* unused */ diff --git a/sys/vfs/gnu/ext2fs/ext2_linux_ialloc.c b/sys/vfs/gnu/ext2fs/ext2_linux_ialloc.c index e095948561..3a7b3ae1f7 100644 --- a/sys/vfs/gnu/ext2fs/ext2_linux_ialloc.c +++ b/sys/vfs/gnu/ext2fs/ext2_linux_ialloc.c @@ -5,7 +5,7 @@ * University of Utah, Department of Computer Science * * $FreeBSD: src/sys/gnu/ext2fs/ext2_linux_ialloc.c,v 1.13.2.2 2001/08/14 18:03:19 gallatin Exp $ - * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2_linux_ialloc.c,v 1.8 2006/03/24 18:35:33 dillon Exp $ + * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2_linux_ialloc.c,v 1.9 2006/04/04 17:34:32 dillon Exp $ */ /* * linux/fs/ext2/ialloc.c @@ -37,9 +37,9 @@ #include #include -#include -#include -#include +#include "quota.h" +#include "inode.h" +#include "ext2mount.h" #include "ext2_extern.h" #include "ext2_fs.h" #include "ext2_fs_sb.h" @@ -70,7 +70,7 @@ struct ext2_group_desc * get_group_desc(struct mount * mp, unsigned int block_group, struct buffer_head **bh) { - struct ext2_sb_info *sb = VFSTOUFS(mp)->um_e2fs; + struct ext2_sb_info *sb = VFSTOEXT2(mp)->um_e2fs; unsigned long group_desc; unsigned long desc; struct ext2_group_desc * gdp; @@ -99,13 +99,13 @@ static void read_inode_bitmap(struct mount *mp, unsigned long block_group, unsigned int bitmap_nr) { - struct ext2_sb_info *sb = VFSTOUFS(mp)->um_e2fs; + struct ext2_sb_info *sb = VFSTOEXT2(mp)->um_e2fs; struct ext2_group_desc * gdp; struct buffer_head * bh; int error; gdp = get_group_desc (mp, block_group, NULL); - if ((error = bread (VFSTOUFS(mp)->um_devvp, + if ((error = bread (VFSTOEXT2(mp)->um_devvp, fsbtodoff(sb, gdp->bg_inode_bitmap), sb->s_blocksize, &bh)) != 0) panic ( "read_inode_bitmap:" @@ -131,7 +131,7 @@ read_inode_bitmap(struct mount *mp, unsigned long block_group, static int load_inode_bitmap(struct mount *mp, unsigned int block_group) { - struct ext2_sb_info *sb = VFSTOUFS(mp)->um_e2fs; + struct ext2_sb_info *sb = VFSTOEXT2(mp)->um_e2fs; int i, j; unsigned long inode_bitmap_number; struct buffer_head * inode_bitmap; @@ -449,14 +449,14 @@ static unsigned long ext2_count_free_inodes(struct mount *mp) { #ifdef EXT2FS_DEBUG - struct ext2_sb_info *sb = VFSTOUFS(mp)->um_e2fs; + struct ext2_sb_info *sb = VFSTOEXT2(mp)->um_e2fs; struct ext2_super_block * es; unsigned long desc_count, bitmap_count, x; int bitmap_nr; struct ext2_group_desc * gdp; int i; - lock_super (VFSTOUFS(mp)->um_devvp); + lock_super (VFSTOEXT2(mp)->um_devvp); es = sb->s_es; desc_count = 0; bitmap_count = 0; @@ -473,10 +473,10 @@ ext2_count_free_inodes(struct mount *mp) } ext2_debug("stored = %lu, computed = %lu, %lu\n", es->s_free_inodes_count, desc_count, bitmap_count); - unlock_super (VFSTOUFS(mp)->um_devvp); + unlock_super (VFSTOEXT2(mp)->um_devvp); return desc_count; #else - return VFSTOUFS(mp)->um_e2fsb->s_free_inodes_count; + return VFSTOEXT2(mp)->um_e2fsb->s_free_inodes_count; #endif } #endif /* unused */ diff --git a/sys/vfs/gnu/ext2fs/ext2_lookup.c b/sys/vfs/gnu/ext2fs/ext2_lookup.c index ef7af4cf55..1e71d2b425 100644 --- a/sys/vfs/gnu/ext2fs/ext2_lookup.c +++ b/sys/vfs/gnu/ext2fs/ext2_lookup.c @@ -5,7 +5,7 @@ * University of Utah, Department of Computer Science * * $FreeBSD: src/sys/gnu/ext2fs/ext2_lookup.c,v 1.21.2.3 2002/11/17 02:02:42 bde Exp $ - * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2_lookup.c,v 1.18 2005/09/14 01:13:35 dillon Exp $ + * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2_lookup.c,v 1.19 2006/04/04 17:34:32 dillon Exp $ */ /* * Copyright (c) 1989, 1993 @@ -56,12 +56,10 @@ #include #include -#include -#include -#include -#include -#include - +#include "quota.h" +#include "inode.h" +#include "dir.h" +#include "ext2mount.h" #include "ext2_extern.h" #include "ext2_fs.h" #include "ext2_fs_sb.h" @@ -346,7 +344,7 @@ ext2_lookup(struct vop_old_lookup_args *ap) * profiling time and hence has been removed in the interest * of simplicity. */ - bmask = VFSTOUFS(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1; + bmask = VFSTOEXT2(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1; if (nameiop != NAMEI_LOOKUP || dp->i_diroff == 0 || dp->i_diroff > dp->i_size) { entryoffsetinblock = 0; @@ -355,7 +353,7 @@ ext2_lookup(struct vop_old_lookup_args *ap) } else { dp->i_offset = dp->i_diroff; if ((entryoffsetinblock = dp->i_offset & bmask) && - (error = UFS_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp))) + (error = EXT2_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp))) return (error); numdirpasses = 2; gd->gd_nchstats->ncs_2passes++; @@ -373,7 +371,7 @@ searchloop: if (bp != NULL) brelse(bp); if ((error = - UFS_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp)) != 0) + EXT2_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp)) != 0) return (error); entryoffsetinblock = 0; } @@ -398,7 +396,7 @@ searchloop: if (ep->rec_len == 0 || (dirchk && ext2_dirbadentry(vdp, ep, entryoffsetinblock))) { int i; - ufs_dirbad(dp, dp->i_offset, "mangled entry"); + ext2_dirbad(dp, dp->i_offset, "mangled entry"); i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)); dp->i_offset += i; entryoffsetinblock += i; @@ -533,7 +531,7 @@ found: */ if (entryoffsetinblock + EXT2_DIR_REC_LEN(ep->name_len) > dp->i_size) { - ufs_dirbad(dp, dp->i_offset, "i_size too small"); + ext2_dirbad(dp, dp->i_offset, "i_size too small"); dp->i_size = entryoffsetinblock+EXT2_DIR_REC_LEN(ep->name_len); dp->i_flag |= IN_CHANGE | IN_UPDATE; } @@ -663,6 +661,19 @@ found: return (0); } +void +ext2_dirbad(struct inode *ip, doff_t offset, char *how) +{ + struct mount *mp; + + mp = ITOV(ip)->v_mount; + printf("%s: bad dir ino %lu at offset %ld: %s\n", + mp->mnt_stat.f_mntfromname, (u_long)ip->i_number, + (long)offset, how); + if ((mp->mnt_flag & MNT_RDONLY) == 0) + panic("ufs_dirbad: bad dir"); +} + /* * Do consistency checking on a directory entry: * record length must be multiple of 4 @@ -758,7 +769,7 @@ ext2_direnter(struct inode *ip, struct vnode *dvp, struct componentname *cnp) auio.uio_td = NULL; error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred); if (DIRBLKSIZ > - VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize) + VFSTOEXT2(dvp->v_mount)->um_mountp->mnt_stat.f_bsize) /* XXX should grow with balloc() */ panic("ext2_direnter: frag size"); else if (!error) { @@ -789,7 +800,7 @@ ext2_direnter(struct inode *ip, struct vnode *dvp, struct componentname *cnp) /* * Get the block containing the space for the new directory entry. */ - if ((error = UFS_BLKATOFF(dvp, (off_t)dp->i_offset, &dirbuf, &bp)) != 0) + if ((error = EXT2_BLKATOFF(dvp, (off_t)dp->i_offset, &dirbuf, &bp)) != 0) return (error); /* * Find space for the new entry. In the simple case, the entry at @@ -835,7 +846,7 @@ ext2_direnter(struct inode *ip, struct vnode *dvp, struct componentname *cnp) error = VOP_BWRITE(bp->b_vp, bp); dp->i_flag |= IN_CHANGE | IN_UPDATE; if (!error && dp->i_endoff && dp->i_endoff < dp->i_size) - error = UFS_TRUNCATE(dvp, (off_t)dp->i_endoff, IO_SYNC, + error = EXT2_TRUNCATE(dvp, (off_t)dp->i_endoff, IO_SYNC, cnp->cn_cred, cnp->cn_td); return (error); } @@ -866,7 +877,7 @@ ext2_dirremove(struct vnode *dvp, struct componentname *cnp) * First entry in block: set d_ino to zero. */ if ((error = - UFS_BLKATOFF(dvp, (off_t)dp->i_offset, (char **)&ep, &bp)) != 0) + EXT2_BLKATOFF(dvp, (off_t)dp->i_offset, (char **)&ep, &bp)) != 0) return (error); ep->inode = 0; error = VOP_BWRITE(bp->b_vp, bp); @@ -876,7 +887,7 @@ ext2_dirremove(struct vnode *dvp, struct componentname *cnp) /* * Collapse new free space into previous entry. */ - if ((error = UFS_BLKATOFF(dvp, (off_t)(dp->i_offset - dp->i_count), + if ((error = EXT2_BLKATOFF(dvp, (off_t)(dp->i_offset - dp->i_count), (char **)&ep, &bp)) != 0) return (error); ep->rec_len += dp->i_reclen; @@ -898,7 +909,7 @@ ext2_dirrewrite(struct inode *dp, struct inode *ip, struct componentname *cnp) struct vnode *vdp = ITOV(dp); int error; - if ((error = UFS_BLKATOFF(vdp, (off_t)dp->i_offset, (char **)&ep, &bp)) != 0) + if ((error = EXT2_BLKATOFF(vdp, (off_t)dp->i_offset, (char **)&ep, &bp)) != 0) return (error); ep->inode = ip->i_number; if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs->s_es, diff --git a/sys/vfs/gnu/ext2fs/ext2_quota.c b/sys/vfs/gnu/ext2fs/ext2_quota.c new file mode 100644 index 0000000000..9994aeda31 --- /dev/null +++ b/sys/vfs/gnu/ext2fs/ext2_quota.c @@ -0,0 +1,940 @@ +/* + * Copyright (c) 1982, 1986, 1990, 1993, 1995 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Robert Elz at The University of Melbourne. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. 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. + * + * @(#)ufs_quota.c 8.5 (Berkeley) 5/20/95 + * $FreeBSD: src/sys/ufs/ufs/ufs_quota.c,v 1.27.2.3 2002/01/15 10:33:32 phk Exp $ + * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2_quota.c,v 1.1 2006/04/04 17:34:32 dillon Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "quota.h" +#include "dinode.h" +#include "inode.h" +#include "ext2mount.h" +#include "ext2_extern.h" + +static MALLOC_DEFINE(M_EXT2DQUOT, "EXT2 quota", "EXT2 quota entries"); + +/* + * Quota name to error message mapping. + */ +static char *quotatypes[] = INITQFNAMES; + +static int ext2_chkdqchg (struct inode *, long, struct ucred *, int); +static int ext2_chkiqchg (struct inode *, long, struct ucred *, int); +static int ext2_dqget (struct vnode *, + u_long, struct ext2mount *, int, struct ext2_dquot **); +static int ext2_dqsync (struct vnode *, struct ext2_dquot *); +static void ext2_dqflush (struct vnode *); + +#ifdef DIAGNOSTIC +static void ext2_dqref (struct ext2_dquot *); +static void ext2_chkdquot (struct inode *); +#endif + +/* + * Set up the quotas for an inode. + * + * This routine completely defines the semantics of quotas. + * If other criterion want to be used to establish quotas, the + * MAXQUOTAS value in quotas.h should be increased, and the + * additional dquots set up here. + */ +int +ext2_getinoquota(struct inode *ip) +{ + struct ext2mount *ump; + struct vnode *vp = ITOV(ip); + int error; + + ump = VFSTOEXT2(vp->v_mount); + /* + * Set up the user quota based on file uid. + * EINVAL means that quotas are not enabled. + */ + if (ip->i_dquot[USRQUOTA] == NODQUOT && + (error = ext2_dqget(vp, ip->i_uid, ump, USRQUOTA, &ip->i_dquot[USRQUOTA])) && + error != EINVAL) + return (error); + /* + * Set up the group quota based on file gid. + * EINVAL means that quotas are not enabled. + */ + if (ip->i_dquot[GRPQUOTA] == NODQUOT && + (error = ext2_dqget(vp, ip->i_gid, ump, GRPQUOTA, &ip->i_dquot[GRPQUOTA])) && + error != EINVAL) + return (error); + return (0); +} + +/* + * Update disk usage, and take corrective action. + */ +int +ext2_chkdq(struct inode *ip, long change, struct ucred *cred, int flags) +{ + struct ext2_dquot *dq; + int i; + int ncurblocks, error; + +#ifdef DIAGNOSTIC + if ((flags & CHOWN) == 0) + ext2_chkdquot(ip); +#endif + if (change == 0) + return (0); + if (change < 0) { + for (i = 0; i < MAXQUOTAS; i++) { + if ((dq = ip->i_dquot[i]) == NODQUOT) + continue; + while (dq->dq_flags & DQ_LOCK) { + dq->dq_flags |= DQ_WANT; + (void) tsleep((caddr_t)dq, 0, "chkdq1", 0); + } + ncurblocks = dq->dq_curblocks + change; + if (ncurblocks >= 0) + dq->dq_curblocks = ncurblocks; + else + dq->dq_curblocks = 0; + dq->dq_flags &= ~DQ_BLKS; + dq->dq_flags |= DQ_MOD; + } + return (0); + } + if ((flags & FORCE) == 0 && cred->cr_uid != 0) { + for (i = 0; i < MAXQUOTAS; i++) { + if ((dq = ip->i_dquot[i]) == NODQUOT) + continue; + error = ext2_chkdqchg(ip, change, cred, i); + if (error) + return (error); + } + } + for (i = 0; i < MAXQUOTAS; i++) { + if ((dq = ip->i_dquot[i]) == NODQUOT) + continue; + while (dq->dq_flags & DQ_LOCK) { + dq->dq_flags |= DQ_WANT; + (void) tsleep((caddr_t)dq, 0, "chkdq2", 0); + } + /* Reset timer when crossing soft limit */ + if (dq->dq_curblocks + change >= dq->dq_bsoftlimit && + dq->dq_curblocks < dq->dq_bsoftlimit) + dq->dq_btime = time_second + + VFSTOEXT2(ITOV(ip)->v_mount)->um_btime[i]; + dq->dq_curblocks += change; + dq->dq_flags |= DQ_MOD; + } + return (0); +} + +/* + * Check for a valid change to a users allocation. + * Issue an error message if appropriate. + */ +static int +ext2_chkdqchg(struct inode *ip, long change, struct ucred *cred, int type) +{ + struct ext2_dquot *dq = ip->i_dquot[type]; + long ncurblocks = dq->dq_curblocks + change; + + /* + * If user would exceed their hard limit, disallow space allocation. + */ + if (ncurblocks >= dq->dq_bhardlimit && dq->dq_bhardlimit) { + if ((dq->dq_flags & DQ_BLKS) == 0 && + ip->i_uid == cred->cr_uid) { + uprintf("\n%s: write failed, %s disk limit reached\n", + ITOV(ip)->v_mount->mnt_stat.f_mntfromname, + quotatypes[type]); + dq->dq_flags |= DQ_BLKS; + } + return (EDQUOT); + } + /* + * If user is over their soft limit for too long, disallow space + * allocation. Reset time limit as they cross their soft limit. + */ + if (ncurblocks >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) { + if (dq->dq_curblocks < dq->dq_bsoftlimit) { + dq->dq_btime = time_second + + VFSTOEXT2(ITOV(ip)->v_mount)->um_btime[type]; + if (ip->i_uid == cred->cr_uid) + uprintf("\n%s: warning, %s %s\n", + ITOV(ip)->v_mount->mnt_stat.f_mntfromname, + quotatypes[type], "disk quota exceeded"); + return (0); + } + if (time_second > dq->dq_btime) { + if ((dq->dq_flags & DQ_BLKS) == 0 && + ip->i_uid == cred->cr_uid) { + uprintf("\n%s: write failed, %s %s\n", + ITOV(ip)->v_mount->mnt_stat.f_mntfromname, + quotatypes[type], + "disk quota exceeded for too long"); + dq->dq_flags |= DQ_BLKS; + } + return (EDQUOT); + } + } + return (0); +} + +/* + * Check the inode limit, applying corrective action. + */ +int +ext2_chkiq(struct inode *ip, long change, struct ucred *cred, int flags) +{ + struct ext2_dquot *dq; + int i; + int ncurinodes, error; + +#ifdef DIAGNOSTIC + if ((flags & CHOWN) == 0) + ext2_chkdquot(ip); +#endif + if (change == 0) + return (0); + if (change < 0) { + for (i = 0; i < MAXQUOTAS; i++) { + if ((dq = ip->i_dquot[i]) == NODQUOT) + continue; + while (dq->dq_flags & DQ_LOCK) { + dq->dq_flags |= DQ_WANT; + (void) tsleep((caddr_t)dq, 0, "chkiq1", 0); + } + ncurinodes = dq->dq_curinodes + change; + if (ncurinodes >= 0) + dq->dq_curinodes = ncurinodes; + else + dq->dq_curinodes = 0; + dq->dq_flags &= ~DQ_INODS; + dq->dq_flags |= DQ_MOD; + } + return (0); + } + if ((flags & FORCE) == 0 && cred->cr_uid != 0) { + for (i = 0; i < MAXQUOTAS; i++) { + if ((dq = ip->i_dquot[i]) == NODQUOT) + continue; + error = ext2_chkiqchg(ip, change, cred, i); + if (error) + return (error); + } + } + for (i = 0; i < MAXQUOTAS; i++) { + if ((dq = ip->i_dquot[i]) == NODQUOT) + continue; + while (dq->dq_flags & DQ_LOCK) { + dq->dq_flags |= DQ_WANT; + (void) tsleep((caddr_t)dq, 0, "chkiq2", 0); + } + /* Reset timer when crossing soft limit */ + if (dq->dq_curinodes + change >= dq->dq_isoftlimit && + dq->dq_curinodes < dq->dq_isoftlimit) + dq->dq_itime = time_second + + VFSTOEXT2(ITOV(ip)->v_mount)->um_itime[i]; + dq->dq_curinodes += change; + dq->dq_flags |= DQ_MOD; + } + return (0); +} + +/* + * Check for a valid change to a users allocation. + * Issue an error message if appropriate. + */ +static int +ext2_chkiqchg(struct inode *ip, long change, struct ucred *cred, int type) +{ + struct ext2_dquot *dq = ip->i_dquot[type]; + long ncurinodes = dq->dq_curinodes + change; + + /* + * If user would exceed their hard limit, disallow inode allocation. + */ + if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) { + if ((dq->dq_flags & DQ_INODS) == 0 && + ip->i_uid == cred->cr_uid) { + uprintf("\n%s: write failed, %s inode limit reached\n", + ITOV(ip)->v_mount->mnt_stat.f_mntfromname, + quotatypes[type]); + dq->dq_flags |= DQ_INODS; + } + return (EDQUOT); + } + /* + * If user is over their soft limit for too long, disallow inode + * allocation. Reset time limit as they cross their soft limit. + */ + if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) { + if (dq->dq_curinodes < dq->dq_isoftlimit) { + dq->dq_itime = time_second + + VFSTOEXT2(ITOV(ip)->v_mount)->um_itime[type]; + if (ip->i_uid == cred->cr_uid) + uprintf("\n%s: warning, %s %s\n", + ITOV(ip)->v_mount->mnt_stat.f_mntfromname, + quotatypes[type], "inode quota exceeded"); + return (0); + } + if (time_second > dq->dq_itime) { + if ((dq->dq_flags & DQ_INODS) == 0 && + ip->i_uid == cred->cr_uid) { + uprintf("\n%s: write failed, %s %s\n", + ITOV(ip)->v_mount->mnt_stat.f_mntfromname, + quotatypes[type], + "inode quota exceeded for too long"); + dq->dq_flags |= DQ_INODS; + } + return (EDQUOT); + } + } + return (0); +} + +#ifdef DIAGNOSTIC +/* + * On filesystems with quotas enabled, it is an error for a file to change + * size and not to have a dquot structure associated with it. + */ +static void +ext2_chkdquot(struct inode *ip) +{ + struct ext2mount *ump = VFSTOEXT2(ITOV(ip)->v_mount); + int i; + + for (i = 0; i < MAXQUOTAS; i++) { + if (ump->um_quotas[i] == NULLVP || + (ump->um_qflags[i] & (QTF_OPENING|QTF_CLOSING))) + continue; + if (ip->i_dquot[i] == NODQUOT) { + vprint("chkdquot: missing dquot", ITOV(ip)); + panic("chkdquot: missing dquot"); + } + } +} +#endif + +/* + * Code to process quotactl commands. + */ + +struct scaninfo { + thread_t td; + int rescan; + int type; +}; + +/* + * Q_QUOTAON - set up a quota file for a particular filesystem. + */ +static int ext2_quotaon_scan(struct mount *mp, struct vnode *vp, void *data); + +int +ext2_quotaon(struct thread *td, struct mount *mp, int type, caddr_t fname) +{ + struct ext2mount *ump = VFSTOEXT2(mp); + struct vnode *vp, **vpp; + struct ext2_dquot *dq; + int error; + struct nlookupdata nd; + struct ucred *cred; + struct scaninfo scaninfo; + + KKASSERT(td->td_proc); + cred = td->td_proc->p_ucred; + + vpp = &ump->um_quotas[type]; + error = nlookup_init(&nd, fname, UIO_USERSPACE, NLC_FOLLOW|NLC_LOCKVP); + if (error == 0) + error = vn_open(&nd, NULL, FREAD|FWRITE, 0); + if (error == 0 && nd.nl_open_vp->v_type != VREG) + error = EACCES; + if (error) { + nlookup_done(&nd); + return (error); + } + vp = nd.nl_open_vp; + nd.nl_open_vp = NULL; + nlookup_done(&nd); + + VOP_UNLOCK(vp, 0, td); + if (*vpp != vp) + ext2_quotaoff(td, mp, type); + ump->um_qflags[type] |= QTF_OPENING; + mp->mnt_flag |= MNT_QUOTA; + vp->v_flag |= VSYSTEM; + *vpp = vp; + /* XXX release duplicate vp if *vpp == vp? */ + /* + * Save the credential of the process that turned on quotas. + * Set up the time limits for this quota. + */ + ump->um_cred[type] = crhold(cred); + ump->um_btime[type] = MAX_DQ_TIME; + ump->um_itime[type] = MAX_IQ_TIME; + if (ext2_dqget(NULLVP, 0, ump, type, &dq) == 0) { + if (dq->dq_btime > 0) + ump->um_btime[type] = dq->dq_btime; + if (dq->dq_itime > 0) + ump->um_itime[type] = dq->dq_itime; + ext2_dqrele(NULLVP, dq); + } + /* + * Search vnodes associated with this mount point, + * adding references to quota file being opened. + * NB: only need to add dquot's for inodes being modified. + */ + scaninfo.rescan = 1; + scaninfo.td = td; + while (scaninfo.rescan) { + scaninfo.rescan = 0; + error = vmntvnodescan(mp, VMSC_GETVP, + NULL, ext2_quotaon_scan, &scaninfo); + if (error) + break; + } + ump->um_qflags[type] &= ~QTF_OPENING; + if (error) + ext2_quotaoff(td, mp, type); + return (error); +} + +static int +ext2_quotaon_scan(struct mount *mp, struct vnode *vp, void *data) +{ + int error; + /*struct scaninfo *info = data;*/ + + if (vp->v_writecount == 0) + return(0); + error = ext2_getinoquota(VTOI(vp)); + return(error); +} + +/* + * Q_QUOTAOFF - turn off disk quotas for a filesystem. + */ + +static int ext2_quotaoff_scan(struct mount *mp, struct vnode *vp, void *data); + +int +ext2_quotaoff(struct thread *td, struct mount *mp, int type) +{ + struct vnode *qvp; + struct ext2mount *ump = VFSTOEXT2(mp); + struct ucred *cred; + int error; + struct scaninfo scaninfo; + + KKASSERT(td->td_proc); + cred = td->td_proc->p_ucred; + + if ((qvp = ump->um_quotas[type]) == NULLVP) + return (0); + ump->um_qflags[type] |= QTF_CLOSING; + + /* + * Search vnodes associated with this mount point, + * deleting any references to quota file being closed. + */ + scaninfo.rescan = 1; + scaninfo.td = td; + scaninfo.type = type; + while (scaninfo.rescan) { + scaninfo.rescan = 0; + vmntvnodescan(mp, VMSC_GETVP, NULL, ext2_quotaoff_scan, &scaninfo); + } + ext2_dqflush(qvp); + qvp->v_flag &= ~VSYSTEM; + error = vn_close(qvp, FREAD|FWRITE, td); + ump->um_quotas[type] = NULLVP; + crfree(ump->um_cred[type]); + ump->um_cred[type] = NOCRED; + ump->um_qflags[type] &= ~QTF_CLOSING; + for (type = 0; type < MAXQUOTAS; type++) { + if (ump->um_quotas[type] != NULLVP) + break; + } + if (type == MAXQUOTAS) + mp->mnt_flag &= ~MNT_QUOTA; + return (error); +} + +static int +ext2_quotaoff_scan(struct mount *mp, struct vnode *vp, void *data) +{ + struct scaninfo *info = data; + struct ext2_dquot *dq; + struct inode *ip; + + if (vp->v_type == VNON) { + return(0); + } + ip = VTOI(vp); + dq = ip->i_dquot[info->type]; + ip->i_dquot[info->type] = NODQUOT; + ext2_dqrele(vp, dq); + return(0); +} + +/* + * Q_GETQUOTA - return current values in a dqblk structure. + */ +int +ext2_getquota(struct mount *mp, u_long id, int type, caddr_t addr) +{ + struct ext2_dquot *dq; + int error; + + error = ext2_dqget(NULLVP, id, VFSTOEXT2(mp), type, &dq); + if (error) + return (error); + error = copyout((caddr_t)&dq->dq_dqb, addr, sizeof (struct ext2_dqblk)); + ext2_dqrele(NULLVP, dq); + return (error); +} + +/* + * Q_SETQUOTA - assign an entire dqblk structure. + */ +int +ext2_setquota(struct mount *mp, u_long id, int type, caddr_t addr) +{ + struct ext2_dquot *dq; + struct ext2_dquot *ndq; + struct ext2mount *ump = VFSTOEXT2(mp); + struct ext2_dqblk newlim; + int error; + + error = copyin(addr, (caddr_t)&newlim, sizeof (struct ext2_dqblk)); + if (error) + return (error); + error = ext2_dqget(NULLVP, id, ump, type, &ndq); + if (error) + return (error); + dq = ndq; + while (dq->dq_flags & DQ_LOCK) { + dq->dq_flags |= DQ_WANT; + (void) tsleep((caddr_t)dq, 0, "setqta", 0); + } + /* + * Copy all but the current values. + * Reset time limit if previously had no soft limit or were + * under it, but now have a soft limit and are over it. + */ + newlim.dqb_curblocks = dq->dq_curblocks; + newlim.dqb_curinodes = dq->dq_curinodes; + if (dq->dq_id != 0) { + newlim.dqb_btime = dq->dq_btime; + newlim.dqb_itime = dq->dq_itime; + } + if (newlim.dqb_bsoftlimit && + dq->dq_curblocks >= newlim.dqb_bsoftlimit && + (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit)) + newlim.dqb_btime = time_second + ump->um_btime[type]; + if (newlim.dqb_isoftlimit && + dq->dq_curinodes >= newlim.dqb_isoftlimit && + (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit)) + newlim.dqb_itime = time_second + ump->um_itime[type]; + dq->dq_dqb = newlim; + if (dq->dq_curblocks < dq->dq_bsoftlimit) + dq->dq_flags &= ~DQ_BLKS; + if (dq->dq_curinodes < dq->dq_isoftlimit) + dq->dq_flags &= ~DQ_INODS; + if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 && + dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0) + dq->dq_flags |= DQ_FAKE; + else + dq->dq_flags &= ~DQ_FAKE; + dq->dq_flags |= DQ_MOD; + ext2_dqrele(NULLVP, dq); + return (0); +} + +/* + * Q_SETUSE - set current inode and block usage. + */ +int +ext2_setuse(struct mount *mp, u_long id, int type, caddr_t addr) +{ + struct ext2_dquot *dq; + struct ext2mount *ump = VFSTOEXT2(mp); + struct ext2_dquot *ndq; + struct ext2_dqblk usage; + int error; + + error = copyin(addr, (caddr_t)&usage, sizeof (struct ext2_dqblk)); + if (error) + return (error); + error = ext2_dqget(NULLVP, id, ump, type, &ndq); + if (error) + return (error); + dq = ndq; + while (dq->dq_flags & DQ_LOCK) { + dq->dq_flags |= DQ_WANT; + (void) tsleep((caddr_t)dq, 0, "setuse", 0); + } + /* + * Reset time limit if have a soft limit and were + * previously under it, but are now over it. + */ + if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit && + usage.dqb_curblocks >= dq->dq_bsoftlimit) + dq->dq_btime = time_second + ump->um_btime[type]; + if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit && + usage.dqb_curinodes >= dq->dq_isoftlimit) + dq->dq_itime = time_second + ump->um_itime[type]; + dq->dq_curblocks = usage.dqb_curblocks; + dq->dq_curinodes = usage.dqb_curinodes; + if (dq->dq_curblocks < dq->dq_bsoftlimit) + dq->dq_flags &= ~DQ_BLKS; + if (dq->dq_curinodes < dq->dq_isoftlimit) + dq->dq_flags &= ~DQ_INODS; + dq->dq_flags |= DQ_MOD; + ext2_dqrele(NULLVP, dq); + return (0); +} + +/* + * Q_SYNC - sync quota files to disk. + */ + +static int ext2_qsync_scan(struct mount *mp, struct vnode *vp, void *data); + +int +ext2_qsync(struct mount *mp) +{ + struct ext2mount *ump = VFSTOEXT2(mp); + struct thread *td = curthread; /* XXX */ + struct scaninfo scaninfo; + int i; + + /* + * Check if the mount point has any quotas. + * If not, simply return. + */ + for (i = 0; i < MAXQUOTAS; i++) + if (ump->um_quotas[i] != NULLVP) + break; + if (i == MAXQUOTAS) + return (0); + /* + * Search vnodes associated with this mount point, + * synchronizing any modified ext2_dquot structures. + */ + scaninfo.rescan = 1; + scaninfo.td = td; + while (scaninfo.rescan) { + scaninfo.rescan = 0; + vmntvnodescan(mp, VMSC_GETVP|VMSC_NOWAIT, + NULL, ext2_qsync_scan, &scaninfo); + } + return (0); +} + +static int +ext2_qsync_scan(struct mount *mp, struct vnode *vp, void *data) +{ + /*struct scaninfo *info = data;*/ + struct ext2_dquot *dq; + /* int error;*/ + int i; + + for (i = 0; i < MAXQUOTAS; i++) { + dq = VTOI(vp)->i_dquot[i]; + if (dq != NODQUOT && (dq->dq_flags & DQ_MOD)) + ext2_dqsync(vp, dq); + } + return(0); +} + +/* + * Code pertaining to management of the in-core dquot data structures. + */ +#define DQHASH(dqvp, id) \ + (&ext2_dqhashtbl[((((intptr_t)(dqvp)) >> 8) + id) & ext2_dqhash]) +static LIST_HEAD(ext2_dqhash, ext2_dquot) *ext2_dqhashtbl; +static u_long ext2_dqhash; + +/* + * Dquot free list. + */ +#define DQUOTINC 5 /* minimum free dquots desired */ +static TAILQ_HEAD(ext2_dqfreelist, ext2_dquot) ext2_dqfreelist; +static long ext2_numdquot, ext2_desireddquot = DQUOTINC; + +/* + * Initialize the quota system. + */ +void +ext2_dqinit(void) +{ + ext2_dqhashtbl = hashinit(desiredvnodes, M_EXT2DQUOT, &ext2_dqhash); + TAILQ_INIT(&ext2_dqfreelist); +} + +/* + * Obtain a dquot structure for the specified identifier and quota file + * reading the information from the file if necessary. + */ +static int +ext2_dqget(struct vnode *vp, u_long id, struct ext2mount *ump, int type, + struct ext2_dquot **dqp) +{ + struct thread *td = curthread; /* XXX */ + struct ext2_dquot *dq; + struct ext2_dqhash *dqh; + struct vnode *dqvp; + struct iovec aiov; + struct uio auio; + int error; + + dqvp = ump->um_quotas[type]; + if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) { + *dqp = NODQUOT; + return (EINVAL); + } + /* + * Check the cache first. + */ + dqh = DQHASH(dqvp, id); + for (dq = dqh->lh_first; dq; dq = dq->dq_hash.le_next) { + if (dq->dq_id != id || + dq->dq_ump->um_quotas[dq->dq_type] != dqvp) + continue; + /* + * Cache hit with no references. Take + * the structure off the free list. + */ + if (dq->dq_cnt == 0) + TAILQ_REMOVE(&ext2_dqfreelist, dq, dq_freelist); + DQREF(dq); + *dqp = dq; + return (0); + } + /* + * Not in cache, allocate a new one. + */ + if (TAILQ_EMPTY(&ext2_dqfreelist) && ext2_numdquot < MAXQUOTAS * desiredvnodes) + ext2_desireddquot += DQUOTINC; + if (ext2_numdquot < ext2_desireddquot) { + dq = (struct ext2_dquot *)malloc(sizeof *dq, M_EXT2DQUOT, M_WAITOK); + bzero((char *)dq, sizeof *dq); + ext2_numdquot++; + } else { + if ((dq = TAILQ_FIRST(&ext2_dqfreelist)) == NULL) { + tablefull("dquot"); + *dqp = NODQUOT; + return (EUSERS); + } + if (dq->dq_cnt || (dq->dq_flags & DQ_MOD)) + panic("dqget: free dquot isn't"); + TAILQ_REMOVE(&ext2_dqfreelist, dq, dq_freelist); + if (dq->dq_ump != NULL) + LIST_REMOVE(dq, dq_hash); + } + /* + * Initialize the contents of the dquot structure. + */ + if (vp != dqvp) + vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, td); + LIST_INSERT_HEAD(dqh, dq, dq_hash); + DQREF(dq); + dq->dq_flags = DQ_LOCK; + dq->dq_id = id; + dq->dq_ump = ump; + dq->dq_type = type; + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + aiov.iov_base = (caddr_t)&dq->dq_dqb; + aiov.iov_len = sizeof (struct ext2_dqblk); + auio.uio_resid = sizeof (struct ext2_dqblk); + auio.uio_offset = (off_t)(id * sizeof (struct ext2_dqblk)); + auio.uio_segflg = UIO_SYSSPACE; + auio.uio_rw = UIO_READ; + auio.uio_td = NULL; + error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]); + if (auio.uio_resid == sizeof(struct ext2_dqblk) && error == 0) + bzero((caddr_t)&dq->dq_dqb, sizeof(struct ext2_dqblk)); + if (vp != dqvp) + VOP_UNLOCK(dqvp, 0, td); + if (dq->dq_flags & DQ_WANT) + wakeup((caddr_t)dq); + dq->dq_flags = 0; + /* + * I/O error in reading quota file, release + * quota structure and reflect problem to caller. + */ + if (error) { + LIST_REMOVE(dq, dq_hash); + ext2_dqrele(vp, dq); + *dqp = NODQUOT; + return (error); + } + /* + * Check for no limit to enforce. + * Initialize time values if necessary. + */ + if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 && + dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0) + dq->dq_flags |= DQ_FAKE; + if (dq->dq_id != 0) { + if (dq->dq_btime == 0) + dq->dq_btime = time_second + ump->um_btime[type]; + if (dq->dq_itime == 0) + dq->dq_itime = time_second + ump->um_itime[type]; + } + *dqp = dq; + return (0); +} + +#ifdef DIAGNOSTIC +/* + * Obtain a reference to a dquot. + */ +static void +ext2_dqref(struct ext2_dquot *dq) +{ + dq->dq_cnt++; +} +#endif + +/* + * Release a reference to a dquot. + */ +void +ext2_dqrele(struct vnode *vp, struct ext2_dquot *dq) +{ + if (dq == NODQUOT) + return; + if (dq->dq_cnt > 1) { + dq->dq_cnt--; + return; + } + if (dq->dq_flags & DQ_MOD) + (void)ext2_dqsync(vp, dq); + if (--dq->dq_cnt > 0) + return; + TAILQ_INSERT_TAIL(&ext2_dqfreelist, dq, dq_freelist); +} + +/* + * Update the disk quota in the quota file. + */ +static int +ext2_dqsync(struct vnode *vp, struct ext2_dquot *dq) +{ + struct thread *td = curthread; /* XXX */ + struct vnode *dqvp; + struct iovec aiov; + struct uio auio; + int error; + + if (dq == NODQUOT) + panic("dqsync: dquot"); + if ((dq->dq_flags & DQ_MOD) == 0) + return (0); + if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP) + panic("dqsync: file"); + if (vp != dqvp) + vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, td); + while (dq->dq_flags & DQ_LOCK) { + dq->dq_flags |= DQ_WANT; + (void) tsleep((caddr_t)dq, 0, "dqsync", 0); + if ((dq->dq_flags & DQ_MOD) == 0) { + if (vp != dqvp) + VOP_UNLOCK(dqvp, 0, td); + return (0); + } + } + dq->dq_flags |= DQ_LOCK; + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + aiov.iov_base = (caddr_t)&dq->dq_dqb; + aiov.iov_len = sizeof (struct ext2_dqblk); + auio.uio_resid = sizeof (struct ext2_dqblk); + auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct ext2_dqblk)); + auio.uio_segflg = UIO_SYSSPACE; + auio.uio_rw = UIO_WRITE; + auio.uio_td = NULL; + error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]); + if (auio.uio_resid && error == 0) + error = EIO; + if (dq->dq_flags & DQ_WANT) + wakeup((caddr_t)dq); + dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT); + if (vp != dqvp) + VOP_UNLOCK(dqvp, 0, td); + return (error); +} + +/* + * Flush all entries from the cache for a particular vnode. + */ +static void +ext2_dqflush(struct vnode *vp) +{ + struct ext2_dquot *dq, *nextdq; + struct ext2_dqhash *dqh; + + /* + * Move all dquot's that used to refer to this quota + * file off their hash chains (they will eventually + * fall off the head of the free list and be re-used). + */ + for (dqh = &ext2_dqhashtbl[ext2_dqhash]; dqh >= ext2_dqhashtbl; dqh--) { + for (dq = dqh->lh_first; dq; dq = nextdq) { + nextdq = dq->dq_hash.le_next; + if (dq->dq_ump->um_quotas[dq->dq_type] != vp) + continue; + if (dq->dq_cnt) + panic("dqflush: stray dquot"); + LIST_REMOVE(dq, dq_hash); + dq->dq_ump = (struct ext2mount *)0; + } + } +} diff --git a/sys/vfs/gnu/ext2fs/ext2_readwrite.c b/sys/vfs/gnu/ext2fs/ext2_readwrite.c index 94b3a31fe0..54cf2d51d9 100644 --- a/sys/vfs/gnu/ext2fs/ext2_readwrite.c +++ b/sys/vfs/gnu/ext2fs/ext2_readwrite.c @@ -38,7 +38,7 @@ * * @(#)ufs_readwrite.c 8.7 (Berkeley) 1/21/94 * $FreeBSD: src/sys/gnu/ext2fs/ext2_readwrite.c,v 1.18.2.2 2000/12/22 18:44:33 dillon Exp $ - * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2_readwrite.c,v 1.9 2006/03/24 18:35:33 dillon Exp $ + * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2_readwrite.c,v 1.10 2006/04/04 17:34:32 dillon Exp $ */ #define BLKSIZE(a, b, c) blksize(a, b, c) @@ -292,12 +292,12 @@ ext2_write(struct vop_write_args *ap) ip->i_mode &= ~(ISUID | ISGID); if (error) { if (ioflag & IO_UNIT) { - UFS_TRUNCATE(vp, osize, + EXT2_TRUNCATE(vp, osize, ioflag & IO_SYNC, ap->a_cred, uio->uio_td); uio->uio_offset -= resid - uio->uio_resid; uio->uio_resid = resid; } } else if (resid > uio->uio_resid && (ioflag & IO_SYNC)) - error = UFS_UPDATE(vp, 1); + error = EXT2_UPDATE(vp, 1); return (error); } diff --git a/sys/vfs/gnu/ext2fs/ext2_subr.c b/sys/vfs/gnu/ext2fs/ext2_subr.c index bd524703be..edf006bb88 100644 --- a/sys/vfs/gnu/ext2fs/ext2_subr.c +++ b/sys/vfs/gnu/ext2fs/ext2_subr.c @@ -38,21 +38,22 @@ * * @(#)ext2_subr.c 8.2 (Berkeley) 9/21/93 * $FreeBSD: src/sys/gnu/ext2fs/ext2_subr.c,v 1.13.2.2 2000/08/03 18:48:27 peter Exp $ - * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2_subr.c,v 1.10 2006/03/24 18:35:33 dillon Exp $ + * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2_subr.c,v 1.11 2006/04/04 17:34:32 dillon Exp $ */ #include -#include "ext2_fs_sb.h" -#include "fs.h" #include #include #include #include -#include "ext2_extern.h" #include -#include -#include + +#include "quota.h" +#include "inode.h" +#include "ext2_extern.h" +#include "ext2_fs_sb.h" +#include "fs.h" #include "opt_ddb.h" diff --git a/sys/vfs/gnu/ext2fs/ext2_vfsops.c b/sys/vfs/gnu/ext2fs/ext2_vfsops.c index 01a5ea957c..c3ce9bd863 100644 --- a/sys/vfs/gnu/ext2fs/ext2_vfsops.c +++ b/sys/vfs/gnu/ext2fs/ext2_vfsops.c @@ -38,7 +38,7 @@ * * @(#)ffs_vfsops.c 8.8 (Berkeley) 4/18/94 * $FreeBSD: src/sys/gnu/ext2fs/ext2_vfsops.c,v 1.63.2.7 2002/07/01 00:18:51 iedowse Exp $ - * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2_vfsops.c,v 1.34 2006/04/01 20:46:53 dillon Exp $ + * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2_vfsops.c,v 1.35 2006/04/04 17:34:32 dillon Exp $ */ #include "opt_quota.h" @@ -56,18 +56,18 @@ #include #include #include +#include + #include #include -#include -#include -#include -#include - -#include +#include "quota.h" +#include "dinode.h" +#include "inode.h" +#include "ext2mount.h" +#include "ext2_extern.h" #include "fs.h" -#include "ext2_extern.h" #include "ext2_fs.h" #include "ext2_fs_sb.h" @@ -79,30 +79,33 @@ static int ext2_fhtovp (struct mount *, struct fid *, struct vnode **); static int ext2_flushfiles (struct mount *mp, int flags, struct thread *td); static int ext2_mount (struct mount *, char *, caddr_t, struct thread *); static int ext2_mountfs (struct vnode *, struct mount *, struct thread *); +static int ext2_root(struct mount *, struct vnode **); static int ext2_reload (struct mount *mountp, struct ucred *cred, struct thread *p); -static int ext2_sbupdate (struct ufsmount *, int); +static int ext2_sbupdate (struct ext2mount *, int); static int ext2_statfs (struct mount *, struct statfs *, struct thread *); static int ext2_sync (struct mount *, int, struct thread *); static int ext2_unmount (struct mount *, int, struct thread *); static int ext2_vget (struct mount *, ino_t, struct vnode **); +static int ext2_init(struct vfsconf *); static int ext2_vptofh (struct vnode *, struct fid *); static MALLOC_DEFINE(M_EXT2NODE, "EXT2 node", "EXT2 vnode private part"); +MALLOC_DEFINE(M_EXT2MNT, "EXT2 mount", "EXT2 mount structure"); static struct vfsops ext2fs_vfsops = { .vfs_mount = ext2_mount, .vfs_unmount = ext2_unmount, - .vfs_root = ufs_root, /* root inode via vget */ - .vfs_quotactl = ufs_quotactl, /* quota operations */ + .vfs_root = ext2_root, /* root inode via vget */ + .vfs_quotactl = ext2_quotactl, /* quota operations */ .vfs_statfs = ext2_statfs, .vfs_sync = ext2_sync, .vfs_vget = ext2_vget, .vfs_fhtovp = ext2_fhtovp, - .vfs_checkexp = ufs_check_export, + .vfs_checkexp = ext2_check_export, .vfs_vptofh = ext2_vptofh, - .vfs_init = ufs_init, - .vfs_uninit = ufs_uninit + .vfs_init = ext2_init, + .vfs_uninit = ext2_uninit }; VFS_SET(ext2fs_vfsops, ext2fs, 0); @@ -117,21 +120,137 @@ static int compute_sb_data (struct vnode * devvp, struct ext2_super_block * es, struct ext2_sb_info * fs); +static int +ext2_root(struct mount *mp, struct vnode **vpp) +{ + struct vnode *nvp; + int error; + + error = VFS_VGET(mp, (ino_t)ROOTINO, &nvp); + if (error) + return (error); + *vpp = nvp; + return (0); +} + +/* + * Do operations associated with quotas + */ +int +ext2_quotactl(struct mount *mp, int cmds, uid_t uid, caddr_t arg, + struct thread *td) +{ +#ifndef QUOTA + return (EOPNOTSUPP); +#else + struct proc *p = td->td_proc; + int cmd, type, error; + + if (p == NULL) + return (EOPNOTSUPP); + + type = cmds & SUBCMDMASK; + cmd = cmds >> SUBCMDSHIFT; + + if (uid == -1) { + switch(type) { + case USRQUOTA: + uid = p->p_ucred->cr_ruid; + break; + case GRPQUOTA: + uid = p->p_ucred->cr_rgid; + break; + default: + return (EINVAL); + } + } + + switch (cmd) { + case Q_SYNC: + break; + case Q_GETQUOTA: + if (uid == p->p_ucred->cr_ruid) + break; + /* fall through */ + default: + if ((error = suser_cred(p->p_ucred, PRISON_ROOT)) != 0) + return (error); + } + + type = cmds & SUBCMDMASK; + if ((uint)type >= MAXQUOTAS) + return (EINVAL); + if (vfs_busy(mp, LK_NOWAIT, td)) + return (0); + + switch (cmd) { + + case Q_QUOTAON: + error = ext2_quotaon(td, mp, type, arg); + break; + + case Q_QUOTAOFF: + error = ext2_quotaoff(td, mp, type); + break; + + case Q_SETQUOTA: + error = ext2_setquota(mp, uid, type, arg); + break; + + case Q_SETUSE: + error = ext2_setuse(mp, uid, type, arg); + break; + + case Q_GETQUOTA: + error = ext2_getquota(mp, uid, type, arg); + break; + + case Q_SYNC: + error = ext2_qsync(mp); + break; + + default: + error = EINVAL; + break; + } + vfs_unbusy(mp, td); + return (error); +#endif +} + +/* + * Initial UFS filesystems, done only once. + */ +int +ext2_init(struct vfsconf *vfsp) +{ + static int done; + + if (done) + return (0); + done = 1; + ext2_ihashinit(); +#ifdef QUOTA + ext2_dqinit(); +#endif + return (0); +} + /* * VFS Operations. * * mount system call * * Parameters: - * data: this is actually a (struct ufs_args *) + * data: this is actually a (struct ext2_args *) */ static int ext2_mount(struct mount *mp, char *path, caddr_t data, struct thread *td) { struct vnode *devvp; - struct ufs_args args; - struct ufsmount *ump = 0; + struct ext2_args args; + struct ext2mount *ump = 0; struct ext2_sb_info *fs; size_t size; int error, flags; @@ -139,7 +258,7 @@ ext2_mount(struct mount *mp, char *path, caddr_t data, struct ucred *cred; struct nlookupdata nd; - if ((error = copyin(data, (caddr_t)&args, sizeof (struct ufs_args))) != 0) + if ((error = copyin(data, (caddr_t)&args, sizeof (struct ext2_args))) != 0) return (error); cred = td->td_proc->p_ucred; @@ -148,7 +267,7 @@ ext2_mount(struct mount *mp, char *path, caddr_t data, * read/write; if there is no device name, that's all we do. */ if (mp->mnt_flag & MNT_UPDATE) { - ump = VFSTOUFS(mp); + ump = VFSTOEXT2(mp); fs = ump->um_e2fs; devvp = ump->um_devvp; error = 0; @@ -267,7 +386,7 @@ ext2_mount(struct mount *mp, char *path, caddr_t data, vrele(devvp); return (error); } - ump = VFSTOUFS(mp); + ump = VFSTOEXT2(mp); fs = ump->um_e2fs; copyinstr(path, fs->fs_fsmnt, sizeof(fs->fs_fsmnt) - 1, &size); bzero(fs->fs_fsmnt + size, sizeof(fs->fs_fsmnt) - size); @@ -414,7 +533,7 @@ compute_sb_data(struct vnode *devvp, struct ext2_super_block *es, V(s_db_per_group) fs->s_group_desc = bsd_malloc(db_count * sizeof (struct buf *), - M_UFSMNT, M_WAITOK); + M_EXT2MNT, M_WAITOK); /* adjust logic_sb_block */ if(fs->s_blocksize > SBSIZE) @@ -429,7 +548,7 @@ compute_sb_data(struct vnode *devvp, struct ext2_super_block *es, if(error) { for (j = 0; j < i; j++) brelse(fs->s_group_desc[j]); - bsd_free(fs->s_group_desc, M_UFSMNT); + bsd_free(fs->s_group_desc, M_EXT2MNT); printf("EXT2-fs: unable to read group descriptors (%d)\n", error); return EIO; } @@ -439,7 +558,7 @@ compute_sb_data(struct vnode *devvp, struct ext2_super_block *es, if(!ext2_check_descriptors(fs)) { for (j = 0; j < db_count; j++) ULCK_BUF(fs->s_group_desc[j]) - bsd_free(fs->s_group_desc, M_UFSMNT); + bsd_free(fs->s_group_desc, M_EXT2MNT); printf("EXT2-fs: (ext2_check_descriptors failure) " "unable to read group descriptors\n"); return EIO; @@ -496,7 +615,7 @@ ext2_reload(struct mount *mountp, struct ucred *cred, struct thread *td) /* * Step 1: invalidate all cached meta-data. */ - devvp = VFSTOUFS(mountp)->um_devvp; + devvp = VFSTOEXT2(mountp)->um_devvp; if (vinvalbuf(devvp, 0, td, 0, 0)) panic("ext2_reload: dirty1"); /* @@ -510,7 +629,7 @@ ext2_reload(struct mount *mountp, struct ucred *cred, struct thread *td) brelse(bp); return (EIO); /* XXX needs translation */ } - fs = VFSTOUFS(mountp)->um_e2fs; + fs = VFSTOEXT2(mountp)->um_e2fs; bcopy(bp->b_data, fs->s_es, sizeof(struct ext2_super_block)); if((error = compute_sb_data(devvp, es, fs)) != 0) { @@ -584,7 +703,7 @@ ext2_reload_scan2(struct mount *mp, struct vnode *vp, void *data) static int ext2_mountfs(struct vnode *devvp, struct mount *mp, struct thread *td) { - struct ufsmount *ump; + struct ext2mount *ump; struct buf *bp; struct ext2_sb_info *fs; struct ext2_super_block * es; @@ -650,7 +769,7 @@ ext2_mountfs(struct vnode *devvp, struct mount *mp, struct thread *td) goto out; } } - ump = bsd_malloc(sizeof *ump, M_UFSMNT, M_WAITOK); + ump = bsd_malloc(sizeof *ump, M_EXT2MNT, M_WAITOK); bzero((caddr_t)ump, sizeof *ump); ump->um_malloctype = M_EXT2NODE; ump->um_blkatoff = ext2_blkatoff; @@ -663,9 +782,9 @@ ext2_mountfs(struct vnode *devvp, struct mount *mp, struct thread *td) while Linux keeps the super block in a locked buffer */ ump->um_e2fs = bsd_malloc(sizeof(struct ext2_sb_info), - M_UFSMNT, M_WAITOK); + M_EXT2MNT, M_WAITOK); ump->um_e2fs->s_es = bsd_malloc(sizeof(struct ext2_super_block), - M_UFSMNT, M_WAITOK); + M_EXT2MNT, M_WAITOK); bcopy(es, ump->um_e2fs->s_es, (u_int)sizeof(struct ext2_super_block)); if ((error = compute_sb_data(devvp, ump->um_e2fs->s_es, ump->um_e2fs))) goto out; @@ -694,7 +813,7 @@ ext2_mountfs(struct vnode *devvp, struct mount *mp, struct thread *td) ump->um_dev = dev; ump->um_devvp = devvp; /* setting those two parameters allows us to use - ufs_bmap w/o changse ! + ext2_bmap w/o changse ! */ ump->um_nindir = EXT2_ADDR_PER_BLOCK(fs); ump->um_bptrtodb = fs->s_es->s_log_block_size + 1; @@ -718,9 +837,9 @@ out: brelse(bp); VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, td); if (ump) { - bsd_free(ump->um_e2fs->s_es, M_UFSMNT); - bsd_free(ump->um_e2fs, M_UFSMNT); - bsd_free(ump, M_UFSMNT); + bsd_free(ump->um_e2fs->s_es, M_EXT2MNT); + bsd_free(ump->um_e2fs, M_EXT2MNT); + bsd_free(ump, M_EXT2MNT); mp->mnt_data = (qaddr_t)0; } return (error); @@ -732,7 +851,7 @@ out: static int ext2_unmount(struct mount *mp, int mntflags, struct thread *td) { - struct ufsmount *ump; + struct ext2mount *ump; struct ext2_sb_info *fs; int error, flags, ronly, i; @@ -744,7 +863,7 @@ ext2_unmount(struct mount *mp, int mntflags, struct thread *td) } if ((error = ext2_flushfiles(mp, flags, td)) != 0) return (error); - ump = VFSTOUFS(mp); + ump = VFSTOEXT2(mp); fs = ump->um_e2fs; ronly = fs->s_rd_only; if (ronly == 0) { @@ -756,7 +875,7 @@ ext2_unmount(struct mount *mp, int mntflags, struct thread *td) /* release buffers containing group descriptors */ for(i = 0; i < fs->s_db_per_group; i++) ULCK_BUF(fs->s_group_desc[i]) - bsd_free(fs->s_group_desc, M_UFSMNT); + bsd_free(fs->s_group_desc, M_EXT2MNT); /* release cached inode/block bitmaps */ for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++) @@ -770,9 +889,9 @@ ext2_unmount(struct mount *mp, int mntflags, struct thread *td) ump->um_devvp->v_rdev->si_mountpoint = NULL; error = VOP_CLOSE(ump->um_devvp, ronly ? FREAD : FREAD|FWRITE, td); vrele(ump->um_devvp); - bsd_free(fs->s_es, M_UFSMNT); - bsd_free(fs, M_UFSMNT); - bsd_free(ump, M_UFSMNT); + bsd_free(fs->s_es, M_EXT2MNT); + bsd_free(fs, M_EXT2MNT); + bsd_free(ump, M_EXT2MNT); mp->mnt_data = (qaddr_t)0; mp->mnt_flag &= ~MNT_LOCAL; return (error); @@ -784,13 +903,13 @@ ext2_unmount(struct mount *mp, int mntflags, struct thread *td) static int ext2_flushfiles(struct mount *mp, int flags, struct thread *td) { - struct ufsmount *ump; + struct ext2mount *ump; int error; #if QUOTA int i; #endif - ump = VFSTOUFS(mp); + ump = VFSTOEXT2(mp); #if QUOTA if (mp->mnt_flag & MNT_QUOTA) { if ((error = vflush(mp, 0, SKIPSYSTEM|flags)) != 0) @@ -798,7 +917,7 @@ ext2_flushfiles(struct mount *mp, int flags, struct thread *td) for (i = 0; i < MAXQUOTAS; i++) { if (ump->um_quotas[i] == NULLVP) continue; - quotaoff(td, mp, i); + ext2_quotaoff(td, mp, i); } /* * Here we fall through to vflush again to ensure @@ -818,12 +937,12 @@ static int ext2_statfs(struct mount *mp, struct statfs *sbp, struct thread *td) { unsigned long overhead; - struct ufsmount *ump; + struct ext2mount *ump; struct ext2_sb_info *fs; struct ext2_super_block *es; int i, nsb; - ump = VFSTOUFS(mp); + ump = VFSTOEXT2(mp); fs = ump->um_e2fs; es = fs->s_es; @@ -874,7 +993,7 @@ static int ext2_sync_scan(struct mount *mp, struct vnode *vp, void *data); static int ext2_sync(struct mount *mp, int waitfor, struct thread *td) { - struct ufsmount *ump = VFSTOUFS(mp); + struct ext2mount *ump = VFSTOEXT2(mp); struct ext2_sb_info *fs; struct scaninfo scaninfo; int error; @@ -908,7 +1027,7 @@ ext2_sync(struct mount *mp, int waitfor, struct thread *td) VOP_UNLOCK(ump->um_devvp, 0, td); } #if QUOTA - qsync(mp); + ext2_qsync(mp); #endif /* * Write back modified superblock. @@ -952,17 +1071,17 @@ ext2_vget(struct mount *mp, ino_t ino, struct vnode **vpp) { struct ext2_sb_info *fs; struct inode *ip; - struct ufsmount *ump; + struct ext2mount *ump; struct buf *bp; struct vnode *vp; dev_t dev; int i, error; int used_blocks; - ump = VFSTOUFS(mp); + ump = VFSTOEXT2(mp); dev = ump->um_dev; restart: - if ((*vpp = ufs_ihashget(dev, ino)) != NULL) + if ((*vpp = ext2_ihashget(dev, ino)) != NULL) return (0); /* @@ -989,7 +1108,7 @@ restart: MALLOC(ip, struct inode *, sizeof(struct inode), M_EXT2NODE, M_WAITOK); /* Allocate a new vnode/inode. */ - if ((error = getnewvnode(VT_UFS, mp, &vp, 0, LK_CANRECURSE)) != 0) { + if ((error = getnewvnode(VT_EXT2FS, mp, &vp, 0, LK_CANRECURSE)) != 0) { if (ext2fs_inode_hash_lock < 0) wakeup(&ext2fs_inode_hash_lock); ext2fs_inode_hash_lock = 0; @@ -1013,7 +1132,7 @@ restart: * sleeping waiting for old data structures to be purged or for the * contents of the disk portion of this inode to be read. */ - ufs_ihashins(ip); + ext2_ihashins(ip); if (ext2fs_inode_hash_lock < 0) wakeup(&ext2fs_inode_hash_lock); @@ -1063,7 +1182,7 @@ printf("ext2_vget(%d) dbn= %d ", ino, fsbtodb(fs, ino_to_fsba(fs, ino))); * Initialize the vnode from the inode, check for aliases. * Note that the underlying vnode may have changed. */ - if ((error = ufs_vinit(mp, &vp)) != 0) { + if ((error = ext2_vinit(mp, &vp)) != 0) { vx_put(vp); *vpp = NULL; return (error); @@ -1104,13 +1223,32 @@ ext2_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp) { struct ufid *ufhp; struct ext2_sb_info *fs; + struct inode *ip; + struct vnode *nvp; + int error; ufhp = (struct ufid *)fhp; - fs = VFSTOUFS(mp)->um_e2fs; + fs = VFSTOEXT2(mp)->um_e2fs; if (ufhp->ufid_ino < ROOTINO || ufhp->ufid_ino > fs->s_groups_count * fs->s_es->s_inodes_per_group) return (ESTALE); - return (ufs_fhtovp(mp, ufhp, vpp)); + + error = VFS_VGET(mp, ufhp->ufid_ino, &nvp); + if (error) { + *vpp = NULLVP; + return (error); + } + ip = VTOI(nvp); + if (ip->i_mode == 0 || + ip->i_gen != ufhp->ufid_gen || + (VFSTOEXT2(mp)->um_i_effnlink_valid ? ip->i_effnlink : + ip->i_nlink) <= 0) { + vput(nvp); + *vpp = NULLVP; + return (ESTALE); + } + *vpp = nvp; + return (0); } /* @@ -1131,11 +1269,37 @@ ext2_vptofh(struct vnode *vp, struct fid *fhp) return (0); } +/* + * This is the generic part of fhtovp called after the underlying + * filesystem has validated the file handle. + * + * Verify that a host should have access to a filesystem. + */ +int +ext2_check_export(struct mount *mp, struct sockaddr *nam, int *exflagsp, + struct ucred **credanonp) +{ + struct netcred *np; + struct ext2mount *ump;; + + ump = VFSTOEXT2(mp); + /* + * Get the export permission structure for this tuple. + */ + np = vfs_export_lookup(mp, &ump->um_export, nam); + if (np == NULL) + return (EACCES); + + *exflagsp = np->netc_exflags; + *credanonp = &np->netc_anon; + return (0); +} + /* * Write a superblock and associated information back to disk. */ static int -ext2_sbupdate(struct ufsmount *mp, int waitfor) +ext2_sbupdate(struct ext2mount *mp, int waitfor) { struct ext2_sb_info *fs = mp->um_e2fs; struct ext2_super_block *es = fs->s_es; diff --git a/sys/vfs/gnu/ext2fs/ext2_vnops.c b/sys/vfs/gnu/ext2fs/ext2_vnops.c index 0575f8a3ed..bc6a82095d 100644 --- a/sys/vfs/gnu/ext2fs/ext2_vnops.c +++ b/sys/vfs/gnu/ext2fs/ext2_vnops.c @@ -44,7 +44,7 @@ * @(#)ufs_vnops.c 8.27 (Berkeley) 5/27/95 * @(#)ext2_vnops.c 8.7 (Berkeley) 2/3/94 * $FreeBSD: src/sys/gnu/ext2fs/ext2_vnops.c,v 1.51.2.2 2003/01/02 17:26:18 bde Exp $ - * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2_vnops.c,v 1.25 2006/02/17 19:18:07 dillon Exp $ + * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2_vnops.c,v 1.26 2006/04/04 17:34:32 dillon Exp $ */ #include "opt_quota.h" @@ -55,33 +55,69 @@ #include #include #include +#include #include +#include #include #include #include #include +#include +#include +#include +#include +#include +#include #include +#include +#include #include #include #include #include + #include #include -#include -#include -#include -#include -#include -#include +#include +#include "dir.h" +#include "quota.h" +#include "inode.h" +#include "ext2mount.h" #include "ext2_fs_sb.h" #include "fs.h" #include "ext2_extern.h" #include "ext2_fs.h" +static int ext2_access (struct vop_access_args *); +static int ext2_advlock (struct vop_advlock_args *); +static int ext2_chmod (struct vnode *, int, struct ucred *, struct thread *); +static int ext2_chown (struct vnode *, uid_t, gid_t, struct ucred *, struct thread *); +static int ext2_close (struct vop_close_args *); +static int ext2_getattr (struct vop_getattr_args *); static int ext2_makeinode (int mode, struct vnode *, struct vnode **, struct componentname *); +static int ext2_mmap (struct vop_mmap_args *); +static int ext2_open (struct vop_open_args *); +static int ext2_pathconf (struct vop_pathconf_args *); +static int ext2_print (struct vop_print_args *); +static int ext2_readlink (struct vop_readlink_args *); +static int ext2_setattr (struct vop_setattr_args *); +static int ext2_strategy (struct vop_strategy_args *); +static int ext2_whiteout (struct vop_old_whiteout_args *); +static int filt_ext2read (struct knote *kn, long hint); +static int filt_ext2write (struct knote *kn, long hint); +static int filt_ext2vnode (struct knote *kn, long hint); +static void filt_ext2detach (struct knote *kn); +static int ext2_kqfilter (struct vop_kqfilter_args *ap); +static int ext2spec_close (struct vop_close_args *); +static int ext2spec_read (struct vop_read_args *); +static int ext2spec_write (struct vop_write_args *); +static int ext2fifo_close (struct vop_close_args *); +static int ext2fifo_kqfilter (struct vop_kqfilter_args *); +static int ext2fifo_read (struct vop_read_args *); +static int ext2fifo_write (struct vop_write_args *); static int ext2_fsync (struct vop_fsync_args *); static int ext2_read (struct vop_read_args *); @@ -97,44 +133,28 @@ static int ext2_symlink (struct vop_old_symlink_args *); static int ext2_getpages (struct vop_getpages_args *); static int ext2_putpages (struct vop_putpages_args *); -/* Global vfs data structures for ufs. */ -struct vnodeopv_entry_desc ext2_vnodeop_entries[] = { - { &vop_default_desc, (vnodeopv_entry_t) ufs_vnoperate }, - { &vop_fsync_desc, (vnodeopv_entry_t) ext2_fsync }, - { &vop_inactive_desc, (vnodeopv_entry_t) ext2_inactive }, - { &vop_old_lookup_desc, (vnodeopv_entry_t) ext2_lookup }, - { &vop_read_desc, (vnodeopv_entry_t) ext2_read }, - { &vop_readdir_desc, (vnodeopv_entry_t) ext2_readdir }, - { &vop_reallocblks_desc, (vnodeopv_entry_t) ext2_reallocblks }, - { &vop_write_desc, (vnodeopv_entry_t) ext2_write }, - { &vop_old_remove_desc, (vnodeopv_entry_t) ext2_remove }, - { &vop_old_link_desc, (vnodeopv_entry_t) ext2_link }, - { &vop_old_rename_desc, (vnodeopv_entry_t) ext2_rename }, - { &vop_old_mkdir_desc, (vnodeopv_entry_t) ext2_mkdir }, - { &vop_old_rmdir_desc, (vnodeopv_entry_t) ext2_rmdir }, - { &vop_old_create_desc, (vnodeopv_entry_t) ext2_create }, - { &vop_old_mknod_desc, (vnodeopv_entry_t) ext2_mknod }, - { &vop_old_symlink_desc, (vnodeopv_entry_t) ext2_symlink }, - { &vop_getpages_desc, (vnodeopv_entry_t) ext2_getpages }, - { &vop_putpages_desc, (vnodeopv_entry_t) ext2_putpages }, - { NULL, NULL } -}; - -struct vnodeopv_entry_desc ext2_specop_entries[] = { - { &vop_default_desc, (vnodeopv_entry_t) ufs_vnoperatespec }, - { &vop_fsync_desc, (vnodeopv_entry_t) ext2_fsync }, - { &vop_inactive_desc, (vnodeopv_entry_t) ext2_inactive }, - { NULL, NULL } -}; +#include "ext2_readwrite.c" -struct vnodeopv_entry_desc ext2_fifoop_entries[] = { - { &vop_default_desc, (vnodeopv_entry_t) ufs_vnoperatefifo }, - { &vop_fsync_desc, (vnodeopv_entry_t) ext2_fsync }, - { &vop_inactive_desc, (vnodeopv_entry_t) ext2_inactive }, - { NULL, NULL } +union _qcvt { + int64_t qcvt; + int32_t val[2]; }; +#define SETHIGH(q, h) { \ + union _qcvt tmp; \ + tmp.qcvt = (q); \ + tmp.val[_QUAD_HIGHWORD] = (h); \ + (q) = tmp.qcvt; \ +} +#define SETLOW(q, l) { \ + union _qcvt tmp; \ + tmp.qcvt = (q); \ + tmp.val[_QUAD_LOWWORD] = (l); \ + (q) = tmp.qcvt; \ +} +#define VN_KNOTE(vp, b) \ + KNOTE(&vp->v_pollinfo.vpi_selinfo.si_note, (b)) -#include "ext2_readwrite.c" +#define OFSFMT(vp) ((vp)->v_mount->mnt_maxsymlinklen <= 0) /* * A virgin directory (no blushing please). @@ -142,11 +162,11 @@ struct vnodeopv_entry_desc ext2_fifoop_entries[] = { * Also, we don't use `struct odirtemplate', since it would just cause * endianness problems. */ -static struct dirtemplate mastertemplate = { +static struct dirtemplate ext2_mastertemplate = { 0, 12, 1, EXT2_FT_DIR, ".", 0, DIRBLKSIZ - 12, 2, EXT2_FT_DIR, ".." }; -static struct dirtemplate omastertemplate = { +static struct dirtemplate ext2_omastertemplate = { 0, 12, 1, EXT2_FT_UNKNOWN, ".", 0, DIRBLKSIZ - 12, 2, EXT2_FT_UNKNOWN, ".." }; @@ -223,7 +243,7 @@ loop: #endif } crit_exit(); - return (UFS_UPDATE(ap->a_vp, ap->a_waitfor == MNT_WAIT)); + return (EXT2_UPDATE(ap->a_vp, ap->a_waitfor == MNT_WAIT)); } static int @@ -357,7 +377,7 @@ ext2_link(struct vop_old_link_args *ap) } ip->i_nlink++; ip->i_flag |= IN_CHANGE; - error = UFS_UPDATE(vp, 1); + error = EXT2_UPDATE(vp, 1); if (!error) error = ext2_direnter(ip, tdvp, cnp); if (error) { @@ -484,7 +504,7 @@ abortit: */ ip->i_nlink++; ip->i_flag |= IN_CHANGE; - if ((error = UFS_UPDATE(fvp, 1)) != 0) { + if ((error = EXT2_UPDATE(fvp, 1)) != 0) { VOP_UNLOCK(fvp, 0, td); goto bad; } @@ -568,7 +588,7 @@ abortit: */ if (xp == NULL) { if (dp->i_dev != ip->i_dev) - panic("ufs_rename: EXDEV"); + panic("ext2_rename: EXDEV"); /* * Account for ".." in new directory. * When source and destination have the same @@ -581,7 +601,7 @@ abortit: } dp->i_nlink++; dp->i_flag |= IN_CHANGE; - error = UFS_UPDATE(tdvp, 1); + error = EXT2_UPDATE(tdvp, 1); if (error) goto bad; } @@ -590,7 +610,7 @@ abortit: if (doingdirectory && newparent) { dp->i_nlink--; dp->i_flag |= IN_CHANGE; - UFS_UPDATE(tdvp, 1); + EXT2_UPDATE(tdvp, 1); } goto bad; } @@ -602,12 +622,12 @@ abortit: vput(tdvp); } else { if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev) - panic("ufs_rename: EXDEV"); + panic("ext2_rename: EXDEV"); /* * Short circuit rename(foo, foo). */ if (xp->i_number == ip->i_number) - panic("ufs_rename: same file"); + panic("ext2_rename: same file"); /* * If the parent directory is "sticky", then the user must * own the parent directory, or the destination of the rename, @@ -672,8 +692,8 @@ abortit: xp->i_nlink--; if (doingdirectory) { if (--xp->i_nlink != 0) - panic("ufs_rename: linked directory"); - error = UFS_TRUNCATE(tvp, (off_t)0, IO_SYNC, + panic("ext2_rename: linked directory"); + error = EXT2_TRUNCATE(tvp, (off_t)0, IO_SYNC, tcnp->cn_cred, tcnp->cn_td); } xp->i_flag |= IN_CHANGE; @@ -707,7 +727,7 @@ abortit: * From name has disappeared. */ if (doingdirectory) - panic("ufs_rename: lost dir entry"); + panic("ext2_rename: lost dir entry"); /* ip->i_flag only sets IN_RENAME if doingdirectory */ vrele(ap->a_fvp); if (fcnp->cn_flags & CNP_PDIRUNLOCK) @@ -726,7 +746,7 @@ abortit: * From name has disappeared. */ if (doingdirectory) - panic("ufs_rename: lost dir entry"); + panic("ext2_rename: lost dir entry"); /* ip->i_flag only sets IN_RENAME if doingdirectory */ vrele(ap->a_fvp); vput(fvp); @@ -752,7 +772,7 @@ abortit: */ if (xp != ip) { if (doingdirectory) - panic("ufs_rename: lost dir entry"); + panic("ext2_rename: lost dir entry"); /* ip->i_flag only sets IN_RENAME if doingdirectory */ } else { /* @@ -769,12 +789,12 @@ abortit: UIO_SYSSPACE, IO_NODELOCKED, tcnp->cn_cred, (int *)0, NULL); if (error == 0) { - /* Like ufs little-endian: */ + /* Like ext2 little-endian: */ namlen = dirbuf.dotdot_type; if (namlen != 2 || dirbuf.dotdot_name[0] != '.' || dirbuf.dotdot_name[1] != '.') { - ufs_dirbad(xp, (doff_t)12, + ext2_dirbad(xp, (doff_t)12, "rename: mangled dir"); } else { dirbuf.dotdot_ino = newparent; @@ -848,7 +868,7 @@ ext2_mkdir(struct vop_old_mkdir_args *ap) * but not have it entered in the parent directory. The entry is * made later after writing "." and ".." entries. */ - error = UFS_VALLOC(dvp, dmode, cnp->cn_cred, &tvp); + error = EXT2_VALLOC(dvp, dmode, cnp->cn_cred, &tvp); if (error) goto out; ip = VTOI(tvp); @@ -891,9 +911,9 @@ ext2_mkdir(struct vop_old_mkdir_args *ap) ip->i_uid = cnp->cn_cred->cr_uid; } #ifdef QUOTA - if ((error = getinoquota(ip)) || - (error = chkiq(ip, 1, ucp, 0))) { - UFS_VFREE(tvp, ip->i_number, dmode); + if ((error = ext2_getinoquota(ip)) || + (error = ext2_chkiq(ip, 1, ucp, 0))) { + EXT2_VFREE(tvp, ip->i_number, dmode); vput(tvp); return (error); } @@ -902,9 +922,9 @@ ext2_mkdir(struct vop_old_mkdir_args *ap) #else ip->i_uid = cnp->cn_cred->cr_uid; #ifdef QUOTA - if ((error = getinoquota(ip)) || - (error = chkiq(ip, 1, cnp->cn_cred, 0))) { - UFS_VFREE(tvp, ip->i_number, dmode); + if ((error = ext2_getinoquota(ip)) || + (error = ext2_chkiq(ip, 1, cnp->cn_cred, 0))) { + EXT2_VFREE(tvp, ip->i_number, dmode); vput(tvp); return (error); } @@ -916,7 +936,7 @@ ext2_mkdir(struct vop_old_mkdir_args *ap) ip->i_nlink = 2; if (cnp->cn_flags & CNP_ISWHITEOUT) ip->i_flags |= UF_OPAQUE; - error = UFS_UPDATE(tvp, 1); + error = EXT2_UPDATE(tvp, 1); /* * Bump link count in parent directory @@ -926,16 +946,16 @@ ext2_mkdir(struct vop_old_mkdir_args *ap) */ dp->i_nlink++; dp->i_flag |= IN_CHANGE; - error = UFS_UPDATE(dvp, 1); + error = EXT2_UPDATE(dvp, 1); if (error) goto bad; /* Initialize directory with "." and ".." from static template. */ if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs->s_es, EXT2_FEATURE_INCOMPAT_FILETYPE)) - dtp = &mastertemplate; + dtp = &ext2_mastertemplate; else - dtp = &omastertemplate; + dtp = &ext2_omastertemplate; dirtemplate = *dtp; dirtemplate.dot_ino = ip->i_number; dirtemplate.dotdot_ino = dp->i_number; @@ -953,8 +973,8 @@ ext2_mkdir(struct vop_old_mkdir_args *ap) dp->i_flag |= IN_CHANGE; goto bad; } - if (DIRBLKSIZ > VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize) - panic("ufs_mkdir: blksize"); /* XXX should grow with balloc() */ + if (DIRBLKSIZ > VFSTOEXT2(dvp->v_mount)->um_mountp->mnt_stat.f_bsize) + panic("ext2_mkdir: blksize"); /* XXX should grow with balloc() */ else { ip->i_size = DIRBLKSIZ; ip->i_flag |= IN_CHANGE; @@ -1042,7 +1062,7 @@ ext2_rmdir(struct vop_old_rmdir_args *ap) * worry about them later. */ ip->i_nlink -= 2; - error = UFS_TRUNCATE(vp, (off_t)0, IO_SYNC, cnp->cn_cred, td); + error = EXT2_TRUNCATE(vp, (off_t)0, IO_SYNC, cnp->cn_cred, td); vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td); out: return (error); @@ -1098,7 +1118,7 @@ ext2_makeinode(int mode, struct vnode *dvp, struct vnode **vpp, if ((mode & IFMT) == 0) mode |= IFREG; - error = UFS_VALLOC(dvp, mode, cnp->cn_cred, &tvp); + error = EXT2_VALLOC(dvp, mode, cnp->cn_cred, &tvp); if (error) { return (error); } @@ -1143,8 +1163,8 @@ ext2_makeinode(int mode, struct vnode *dvp, struct vnode **vpp, #ifdef QUOTA if ((error = getinoquota(ip)) || - (error = chkiq(ip, 1, ucp, 0))) { - UFS_VFREE(tvp, ip->i_number, mode); + (error = ext2_chkiq(ip, 1, ucp, 0))) { + EXT2_VFREE(tvp, ip->i_number, mode); vput(tvp); return (error); } @@ -1153,9 +1173,9 @@ ext2_makeinode(int mode, struct vnode *dvp, struct vnode **vpp, #else ip->i_uid = cnp->cn_cred->cr_uid; #ifdef QUOTA - if ((error = getinoquota(ip)) || - (error = chkiq(ip, 1, cnp->cn_cred, 0))) { - UFS_VFREE(tvp, ip->i_number, mode); + if ((error = ext2_getinoquota(ip)) || + (error = ext2_chkiq(ip, 1, cnp->cn_cred, 0))) { + EXT2_VFREE(tvp, ip->i_number, mode); vput(tvp); return (error); } @@ -1175,7 +1195,7 @@ ext2_makeinode(int mode, struct vnode *dvp, struct vnode **vpp, /* * Make sure inode goes to disk before directory entry. */ - error = UFS_UPDATE(tvp, 1); + error = EXT2_UPDATE(tvp, 1); if (error) goto bad; error = ext2_direnter(ip, dvp, cnp); @@ -1221,3 +1241,1058 @@ ext2_putpages(struct vop_putpages_args *ap) return (vnode_pager_generic_putpages(ap->a_vp, ap->a_m, ap->a_count, ap->a_sync, ap->a_rtvals)); } + +void +ext2_itimes(struct vnode *vp) +{ + struct inode *ip; + struct timespec ts; + + ip = VTOI(vp); + if ((ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_UPDATE)) == 0) + return; + if ((vp->v_type == VBLK || vp->v_type == VCHR) && !DOINGSOFTDEP(vp)) + ip->i_flag |= IN_LAZYMOD; + else + ip->i_flag |= IN_MODIFIED; + if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { + vfs_timestamp(&ts); + if (ip->i_flag & IN_ACCESS) { + ip->i_atime = ts.tv_sec; + ip->i_atimensec = ts.tv_nsec; + } + if (ip->i_flag & IN_UPDATE) { + ip->i_mtime = ts.tv_sec; + ip->i_mtimensec = ts.tv_nsec; + ip->i_modrev++; + } + if (ip->i_flag & IN_CHANGE) { + ip->i_ctime = ts.tv_sec; + ip->i_ctimensec = ts.tv_nsec; + } + } + ip->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE); +} + +/* + * Open called. + * + * Nothing to do. + * + * ext2_open(struct vnode *a_vp, int a_mode, struct ucred *a_cred, + * struct thread *a_td) + */ +/* ARGSUSED */ +static +int +ext2_open(struct vop_open_args *ap) +{ + struct vnode *vp = ap->a_vp; + + /* + * Files marked append-only must be opened for appending. + */ + if ((VTOI(vp)->i_flags & APPEND) && + (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE) { + return (EPERM); + } + + /* + * The buffer cache is used for VREG and VDIR files + */ + if (vp->v_type == VREG || vp->v_type == VDIR) + vinitvmio(vp); + + return (vop_stdopen(ap)); +} + +/* + * Close called. + * + * Update the times on the inode. + * + * ext2_close(struct vnode *a_vp, int a_fflag, struct ucred *a_cred, + * struct thread *a_td) + */ +/* ARGSUSED */ +static +int +ext2_close(struct vop_close_args *ap) +{ + struct vnode *vp = ap->a_vp; + + if (vp->v_usecount > 1) + ext2_itimes(vp); + return (vop_stdclose(ap)); +} + +/* + * ext2_access(struct vnode *a_vp, int a_mode, struct ucred *a_cred, + * struct thread *a_td) + */ +static +int +ext2_access(struct vop_access_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct inode *ip = VTOI(vp); + struct ucred *cred = ap->a_cred; + mode_t mask, mode = ap->a_mode; + gid_t *gp; + int i; +#ifdef QUOTA + int error; +#endif + + /* + * Disallow write attempts on read-only filesystems; + * unless the file is a socket, fifo, or a block or + * character device resident on the filesystem. + */ + if (mode & VWRITE) { + switch (vp->v_type) { + case VDIR: + case VLNK: + case VREG: + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return (EROFS); +#ifdef QUOTA + if ((error = ext2_getinoquota(ip)) != 0) + return (error); +#endif + break; + default: + break; + } + } + + /* If immutable bit set, nobody gets to write it. */ + if ((mode & VWRITE) && (ip->i_flags & IMMUTABLE)) + return (EPERM); + + /* Otherwise, user id 0 always gets access. */ + if (cred->cr_uid == 0) + return (0); + + mask = 0; + + /* Otherwise, check the owner. */ + if (cred->cr_uid == ip->i_uid) { + if (mode & VEXEC) + mask |= S_IXUSR; + if (mode & VREAD) + mask |= S_IRUSR; + if (mode & VWRITE) + mask |= S_IWUSR; + return ((ip->i_mode & mask) == mask ? 0 : EACCES); + } + + /* Otherwise, check the groups. */ + for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++) + if (ip->i_gid == *gp) { + if (mode & VEXEC) + mask |= S_IXGRP; + if (mode & VREAD) + mask |= S_IRGRP; + if (mode & VWRITE) + mask |= S_IWGRP; + return ((ip->i_mode & mask) == mask ? 0 : EACCES); + } + + /* Otherwise, check everyone else. */ + if (mode & VEXEC) + mask |= S_IXOTH; + if (mode & VREAD) + mask |= S_IROTH; + if (mode & VWRITE) + mask |= S_IWOTH; + return ((ip->i_mode & mask) == mask ? 0 : EACCES); +} + +/* + * ext2_getattr(struct vnode *a_vp, struct vattr *a_vap, + * struct thread *a_td) + */ +/* ARGSUSED */ +static +int +ext2_getattr(struct vop_getattr_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct inode *ip = VTOI(vp); + struct vattr *vap = ap->a_vap; + + /* + * This may cause i_fsmid to be updated even if no change (0) + * is returned, but we should only write out the inode if non-zero + * is returned and if the mount is read-write. + */ + if (cache_check_fsmid_vp(vp, &ip->i_fsmid) && + (vp->v_mount->mnt_flag & MNT_RDONLY) == 0 + ) { + ip->i_flag |= IN_LAZYMOD; + } + + ext2_itimes(vp); + /* + * Copy from inode table + */ + vap->va_fsid = dev2udev(ip->i_dev); + vap->va_fileid = ip->i_number; + vap->va_mode = ip->i_mode & ~IFMT; + vap->va_nlink = VFSTOEXT2(vp->v_mount)->um_i_effnlink_valid ? + ip->i_effnlink : ip->i_nlink; + vap->va_uid = ip->i_uid; + vap->va_gid = ip->i_gid; + vap->va_rdev = ip->i_rdev; + vap->va_size = ip->i_din.di_size; + vap->va_atime.tv_sec = ip->i_atime; + vap->va_atime.tv_nsec = ip->i_atimensec; + vap->va_mtime.tv_sec = ip->i_mtime; + vap->va_mtime.tv_nsec = ip->i_mtimensec; + vap->va_ctime.tv_sec = ip->i_ctime; + vap->va_ctime.tv_nsec = ip->i_ctimensec; + vap->va_flags = ip->i_flags; + vap->va_gen = ip->i_gen; + vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize; + vap->va_bytes = dbtob((u_quad_t)ip->i_blocks); + vap->va_type = IFTOVT(ip->i_mode); + vap->va_filerev = ip->i_modrev; + vap->va_fsmid = ip->i_fsmid; + return (0); +} + +/* + * Set attribute vnode op. called from several syscalls + * + * ext2_setattr(struct vnode *a_vp, struct vattr *a_vap, + * struct ucred *a_cred, struct thread *a_td) + */ +static +int +ext2_setattr(struct vop_setattr_args *ap) +{ + struct vattr *vap = ap->a_vap; + struct vnode *vp = ap->a_vp; + struct inode *ip = VTOI(vp); + struct ucred *cred = ap->a_cred; + int error; + + /* + * Check for unsettable attributes. + */ + if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) || + (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) || + (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) || + ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) { + return (EINVAL); + } + if (vap->va_flags != VNOVAL) { + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return (EROFS); + if (cred->cr_uid != ip->i_uid && + (error = suser_cred(cred, PRISON_ROOT))) + return (error); + /* + * Note that a root chflags becomes a user chflags when + * we are jailed, unless the jail.chflags_allowed sysctl + * is set. + */ + if (cred->cr_uid == 0 && + (!jailed(cred) || jail_chflags_allowed)) { + if ((ip->i_flags + & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND)) && + securelevel > 0) + return (EPERM); + ip->i_flags = vap->va_flags; + } else { + if (ip->i_flags + & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND) || + (vap->va_flags & UF_SETTABLE) != vap->va_flags) + return (EPERM); + ip->i_flags &= SF_SETTABLE; + ip->i_flags |= (vap->va_flags & UF_SETTABLE); + } + ip->i_flag |= IN_CHANGE; + if (vap->va_flags & (IMMUTABLE | APPEND)) + return (0); + } + if (ip->i_flags & (IMMUTABLE | APPEND)) + return (EPERM); + /* + * Go through the fields and update iff not VNOVAL. + */ + if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) { + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return (EROFS); + if ((error = ext2_chown(vp, vap->va_uid, vap->va_gid, cred, ap->a_td)) != 0) + return (error); + } + if (vap->va_size != VNOVAL) { + /* + * Disallow write attempts on read-only filesystems; + * unless the file is a socket, fifo, or a block or + * character device resident on the filesystem. + */ + switch (vp->v_type) { + case VDIR: + return (EISDIR); + case VLNK: + case VREG: + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return (EROFS); + break; + default: + break; + } + if ((error = EXT2_TRUNCATE(vp, vap->va_size, 0, cred, ap->a_td)) != 0) + return (error); + } + ip = VTOI(vp); + if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) { + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return (EROFS); + if (cred->cr_uid != ip->i_uid && + (error = suser_cred(cred, PRISON_ROOT)) && + ((vap->va_vaflags & VA_UTIMES_NULL) == 0 || + (error = VOP_ACCESS(vp, VWRITE, cred, ap->a_td)))) + return (error); + if (vap->va_atime.tv_sec != VNOVAL) + ip->i_flag |= IN_ACCESS; + if (vap->va_mtime.tv_sec != VNOVAL) + ip->i_flag |= IN_CHANGE | IN_UPDATE; + ext2_itimes(vp); + if (vap->va_atime.tv_sec != VNOVAL) { + ip->i_atime = vap->va_atime.tv_sec; + ip->i_atimensec = vap->va_atime.tv_nsec; + } + if (vap->va_mtime.tv_sec != VNOVAL) { + ip->i_mtime = vap->va_mtime.tv_sec; + ip->i_mtimensec = vap->va_mtime.tv_nsec; + } + error = EXT2_UPDATE(vp, 0); + if (error) + return (error); + } + error = 0; + if (vap->va_mode != (mode_t)VNOVAL) { + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return (EROFS); + error = ext2_chmod(vp, (int)vap->va_mode, cred, ap->a_td); + } + VN_KNOTE(vp, NOTE_ATTRIB); + return (error); +} + +/* + * Change the mode on a file. + * Inode must be locked before calling. + */ +static int +ext2_chmod(struct vnode *vp, int mode, struct ucred *cred, struct thread *td) +{ + struct inode *ip = VTOI(vp); + int error; + + if (cred->cr_uid != ip->i_uid) { + error = suser_cred(cred, PRISON_ROOT); + if (error) + return (error); + } + if (cred->cr_uid) { + if (vp->v_type != VDIR && (mode & S_ISTXT)) + return (EFTYPE); + if (!groupmember(ip->i_gid, cred) && (mode & ISGID)) + return (EPERM); + } + ip->i_mode &= ~ALLPERMS; + ip->i_mode |= (mode & ALLPERMS); + ip->i_flag |= IN_CHANGE; + return (0); +} + +/* + * Perform chown operation on inode ip; + * inode must be locked prior to call. + */ +static int +ext2_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred, + struct thread *td) +{ + struct inode *ip = VTOI(vp); + uid_t ouid; + gid_t ogid; + int error = 0; +#ifdef QUOTA + int i; + long change; +#endif + + if (uid == (uid_t)VNOVAL) + uid = ip->i_uid; + if (gid == (gid_t)VNOVAL) + gid = ip->i_gid; + /* + * If we don't own the file, are trying to change the owner + * of the file, or are not a member of the target group, + * the caller must be superuser or the call fails. + */ + if ((cred->cr_uid != ip->i_uid || uid != ip->i_uid || + (gid != ip->i_gid && !(cred->cr_gid == gid || + groupmember((gid_t)gid, cred)))) && + (error = suser_cred(cred, PRISON_ROOT))) + return (error); + ogid = ip->i_gid; + ouid = ip->i_uid; +#ifdef QUOTA + if ((error = ext2_getinoquota(ip)) != 0) + return (error); + if (ouid == uid) { + ext2_dqrele(vp, ip->i_dquot[USRQUOTA]); + ip->i_dquot[USRQUOTA] = NODQUOT; + } + if (ogid == gid) { + ext2_dqrele(vp, ip->i_dquot[GRPQUOTA]); + ip->i_dquot[GRPQUOTA] = NODQUOT; + } + change = ip->i_blocks; + (void) ext2_chkdq(ip, -change, cred, CHOWN); + (void) ext2_chkiq(ip, -1, cred, CHOWN); + for (i = 0; i < MAXQUOTAS; i++) { + ext2_dqrele(vp, ip->i_dquot[i]); + ip->i_dquot[i] = NODQUOT; + } +#endif + ip->i_gid = gid; + ip->i_uid = uid; +#ifdef QUOTA + if ((error = ext2_getinoquota(ip)) == 0) { + if (ouid == uid) { + ext2_dqrele(vp, ip->i_dquot[USRQUOTA]); + ip->i_dquot[USRQUOTA] = NODQUOT; + } + if (ogid == gid) { + ext2_dqrele(vp, ip->i_dquot[GRPQUOTA]); + ip->i_dquot[GRPQUOTA] = NODQUOT; + } + if ((error = ext2_chkdq(ip, change, cred, CHOWN)) == 0) { + if ((error = ext2_chkiq(ip, 1, cred, CHOWN)) == 0) + goto good; + else + (void)ext2_chkdq(ip, -change, cred, CHOWN|FORCE); + } + for (i = 0; i < MAXQUOTAS; i++) { + ext2_dqrele(vp, ip->i_dquot[i]); + ip->i_dquot[i] = NODQUOT; + } + } + ip->i_gid = ogid; + ip->i_uid = ouid; + if (ext2_getinoquota(ip) == 0) { + if (ouid == uid) { + ext2_dqrele(vp, ip->i_dquot[USRQUOTA]); + ip->i_dquot[USRQUOTA] = NODQUOT; + } + if (ogid == gid) { + ext2_dqrele(vp, ip->i_dquot[GRPQUOTA]); + ip->i_dquot[GRPQUOTA] = NODQUOT; + } + (void) ext2_chkdq(ip, change, cred, FORCE|CHOWN); + (void) ext2_chkiq(ip, 1, cred, FORCE|CHOWN); + (void) ext2_getinoquota(ip); + } + return (error); +good: + if (ext2_getinoquota(ip)) + panic("ext2_chown: lost quota"); +#endif /* QUOTA */ + ip->i_flag |= IN_CHANGE; + if (cred->cr_uid != 0 && (ouid != uid || ogid != gid)) + ip->i_mode &= ~(ISUID | ISGID); + return (0); +} + +/* + * Mmap a file + * + * NB Currently unsupported. + * + * ext2_mmap(struct vnode *a_vp, int a_fflags, struct ucred *a_cred, + * struct thread *a_td) + */ +/* ARGSUSED */ +static +int +ext2_mmap(struct vop_mmap_args *ap) +{ + return (EINVAL); +} + +/* + * whiteout vnode call + * + * ext2_whiteout(struct vnode *a_dvp, struct componentname *a_cnp, int a_flags) + */ +static +int +ext2_whiteout(struct vop_old_whiteout_args *ap) +{ + return (EOPNOTSUPP); +} + +/* + * Return target name of a symbolic link + * + * ext2_readlink(struct vnode *a_vp, struct uio *a_uio, struct ucred *a_cred) + */ +static +int +ext2_readlink(struct vop_readlink_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct inode *ip = VTOI(vp); + int isize; + + isize = ip->i_size; + if ((isize < vp->v_mount->mnt_maxsymlinklen) || + (ip->i_din.di_blocks == 0)) { /* XXX - for old fastlink support */ + uiomove((char *)ip->i_shortlink, isize, ap->a_uio); + return (0); + } + + /* + * Perform the equivalent of an OPEN on vp so we can issue a + * VOP_READ. + */ + if (vp->v_object == NULL) + vinitvmio(vp); + return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred)); +} + +/* + * Calculate the logical to physical mapping if not done already, + * then call the device strategy routine. + * + * In order to be able to swap to a file, the VOP_BMAP operation may not + * deadlock on memory. See ext2_bmap() for details. + * + * ext2_strategy(struct vnode *a_vp, struct bio *a_bio) + */ +static +int +ext2_strategy(struct vop_strategy_args *ap) +{ + struct bio *bio = ap->a_bio; + struct bio *nbio; + struct buf *bp = bio->bio_buf; + struct vnode *vp = ap->a_vp; + struct inode *ip; + int error; + + ip = VTOI(vp); + if (vp->v_type == VBLK || vp->v_type == VCHR) + panic("ext2_strategy: spec"); + nbio = push_bio(bio); + if (nbio->bio_offset == NOOFFSET) { + error = VOP_BMAP(vp, bio->bio_offset, NULL, &nbio->bio_offset, + NULL, NULL); + if (error) { + bp->b_error = error; + bp->b_flags |= B_ERROR; + /* I/O was never started on nbio, must biodone(bio) */ + biodone(bio); + return (error); + } + if (nbio->bio_offset == NOOFFSET) + vfs_bio_clrbuf(bp); + } + if (nbio->bio_offset == NOOFFSET) { + /* I/O was never started on nbio, must biodone(bio) */ + biodone(bio); + return (0); + } + vn_strategy(ip->i_devvp, nbio); + return (0); +} + +/* + * Print out the contents of an inode. + * + * ext2_print(struct vnode *a_vp) + */ +static +int +ext2_print(struct vop_print_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct inode *ip = VTOI(vp); + + printf("tag VT_EXT2FS, ino %lu, on dev %s (%d, %d)", + (u_long)ip->i_number, devtoname(ip->i_dev), major(ip->i_dev), + minor(ip->i_dev)); + if (vp->v_type == VFIFO) + fifo_printinfo(vp); + lockmgr_printinfo(&vp->v_lock); + printf("\n"); + return (0); +} + +/* + * Read wrapper for special devices. + * + * ext2spec_read(struct vnode *a_vp, struct uio *a_uio, int a_ioflag, + * struct ucred *a_cred) + */ +static +int +ext2spec_read(struct vop_read_args *ap) +{ + int error, resid; + struct inode *ip; + struct uio *uio; + + uio = ap->a_uio; + resid = uio->uio_resid; + error = VOCALL(spec_vnode_vops, &ap->a_head); + /* + * The inode may have been revoked during the call, so it must not + * be accessed blindly here or in the other wrapper functions. + */ + ip = VTOI(ap->a_vp); + if (ip != NULL && (uio->uio_resid != resid || (error == 0 && resid != 0))) + ip->i_flag |= IN_ACCESS; + return (error); +} + +/* + * Write wrapper for special devices. + * + * ext2spec_write(struct vnode *a_vp, struct uio *a_uio, int a_ioflag, + * struct ucred *a_cred) + */ +static +int +ext2spec_write(struct vop_write_args *ap) +{ + int error, resid; + struct inode *ip; + struct uio *uio; + + uio = ap->a_uio; + resid = uio->uio_resid; + error = VOCALL(spec_vnode_vops, &ap->a_head); + ip = VTOI(ap->a_vp); + if (ip != NULL && (uio->uio_resid != resid || (error == 0 && resid != 0))) + VTOI(ap->a_vp)->i_flag |= IN_CHANGE | IN_UPDATE; + return (error); +} + +/* + * Close wrapper for special devices. + * + * Update the times on the inode then do device close. + * + * ext2spec_close(struct vnode *a_vp, int a_fflag, struct ucred *a_cred, + * struct thread *a_td) + */ +static +int +ext2spec_close(struct vop_close_args *ap) +{ + struct vnode *vp = ap->a_vp; + + if (vp->v_usecount > 1) + ext2_itimes(vp); + return (VOCALL(spec_vnode_vops, &ap->a_head)); +} + +/* + * Read wrapper for fifos. + * + * ext2fifo_read(struct vnode *a_vp, struct uio *a_uio, int a_ioflag, + * struct ucred *a_cred) + */ +static +int +ext2fifo_read(struct vop_read_args *ap) +{ + int error, resid; + struct inode *ip; + struct uio *uio; + + uio = ap->a_uio; + resid = uio->uio_resid; + error = VOCALL(fifo_vnode_vops, &ap->a_head); + ip = VTOI(ap->a_vp); + if ((ap->a_vp->v_mount->mnt_flag & MNT_NOATIME) == 0 && ip != NULL && + (uio->uio_resid != resid || (error == 0 && resid != 0))) + VTOI(ap->a_vp)->i_flag |= IN_ACCESS; + return (error); +} + +/* + * Write wrapper for fifos. + * + * ext2fifo_write(struct vnode *a_vp, struct uio *a_uio, int a_ioflag, + * struct ucred *a_cred) + */ +static +int +ext2fifo_write(struct vop_write_args *ap) +{ + int error, resid; + struct inode *ip; + struct uio *uio; + + uio = ap->a_uio; + resid = uio->uio_resid; + error = VOCALL(fifo_vnode_vops, &ap->a_head); + ip = VTOI(ap->a_vp); + if (ip != NULL && (uio->uio_resid != resid || (error == 0 && resid != 0))) + VTOI(ap->a_vp)->i_flag |= IN_CHANGE | IN_UPDATE; + return (error); +} + +/* + * Close wrapper for fifos. + * + * Update the times on the inode then do device close. + * + * ext2fifo_close(struct vnode *a_vp, int a_fflag, struct ucred *a_cred, + * struct thread *a_td) + */ +static +int +ext2fifo_close(struct vop_close_args *ap) +{ + struct vnode *vp = ap->a_vp; + + if (vp->v_usecount > 1) + ext2_itimes(vp); + return (VOCALL(fifo_vnode_vops, &ap->a_head)); +} + +/* + * Kqfilter wrapper for fifos. + * + * Fall through to ext2 kqfilter routines if needed + */ +static +int +ext2fifo_kqfilter(struct vop_kqfilter_args *ap) +{ + int error; + + error = VOCALL(fifo_vnode_vops, &ap->a_head); + if (error) + error = ext2_kqfilter(ap); + return (error); +} + +/* + * Return POSIX pathconf information applicable to ext2 filesystems. + * + * ext2_pathconf(struct vnode *a_vp, int a_name, int *a_retval) + */ +static +int +ext2_pathconf(struct vop_pathconf_args *ap) +{ + switch (ap->a_name) { + case _PC_LINK_MAX: + *ap->a_retval = LINK_MAX; + return (0); + case _PC_NAME_MAX: + *ap->a_retval = NAME_MAX; + return (0); + case _PC_PATH_MAX: + *ap->a_retval = PATH_MAX; + return (0); + case _PC_PIPE_BUF: + *ap->a_retval = PIPE_BUF; + return (0); + case _PC_CHOWN_RESTRICTED: + *ap->a_retval = 1; + return (0); + case _PC_NO_TRUNC: + *ap->a_retval = 1; + return (0); + default: + return (EINVAL); + } + /* NOTREACHED */ +} + +/* + * Advisory record locking support + * + * ext2_advlock(struct vnode *a_vp, caddr_t a_id, int a_op, struct flock *a_fl, + * int a_flags) + */ +static +int +ext2_advlock(struct vop_advlock_args *ap) +{ + struct inode *ip = VTOI(ap->a_vp); + + return (lf_advlock(ap, &(ip->i_lockf), ip->i_size)); +} + +/* + * Initialize the vnode associated with a new inode, handle aliased + * vnodes. + */ +int +ext2_vinit(struct mount *mntp, struct vnode **vpp) +{ + struct inode *ip; + struct vnode *vp; + struct timeval tv; + + vp = *vpp; + ip = VTOI(vp); + + switch(vp->v_type = IFTOVT(ip->i_mode)) { + case VCHR: + case VBLK: + vp->v_ops = &mntp->mnt_vn_spec_ops; + addaliasu(vp, ip->i_rdev); + break; + case VFIFO: + vp->v_ops = &mntp->mnt_vn_fifo_ops; + break; + default: + break; + + } + + if (ip->i_number == ROOTINO) + vp->v_flag |= VROOT; + /* + * Initialize modrev times + */ + getmicrouptime(&tv); + SETHIGH(ip->i_modrev, tv.tv_sec); + SETLOW(ip->i_modrev, tv.tv_usec * 4294); + *vpp = vp; + return (0); +} + +static struct filterops ext2read_filtops = + { 1, NULL, filt_ext2detach, filt_ext2read }; +static struct filterops ext2write_filtops = + { 1, NULL, filt_ext2detach, filt_ext2write }; +static struct filterops ext2vnode_filtops = + { 1, NULL, filt_ext2detach, filt_ext2vnode }; + +/* + * ext2_kqfilter(struct vnode *a_vp, struct knote *a_kn) + */ +static int +ext2_kqfilter(struct vop_kqfilter_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct knote *kn = ap->a_kn; + lwkt_tokref ilock; + + switch (kn->kn_filter) { + case EVFILT_READ: + kn->kn_fop = &ext2read_filtops; + break; + case EVFILT_WRITE: + kn->kn_fop = &ext2write_filtops; + break; + case EVFILT_VNODE: + kn->kn_fop = &ext2vnode_filtops; + break; + default: + return (1); + } + + kn->kn_hook = (caddr_t)vp; + + lwkt_gettoken(&ilock, &vp->v_pollinfo.vpi_token); + SLIST_INSERT_HEAD(&vp->v_pollinfo.vpi_selinfo.si_note, kn, kn_selnext); + lwkt_reltoken(&ilock); + + return (0); +} + +static void +filt_ext2detach(struct knote *kn) +{ + struct vnode *vp = (struct vnode *)kn->kn_hook; + lwkt_tokref ilock; + + lwkt_gettoken(&ilock, &vp->v_pollinfo.vpi_token); + SLIST_REMOVE(&vp->v_pollinfo.vpi_selinfo.si_note, + kn, knote, kn_selnext); + lwkt_reltoken(&ilock); +} + +/*ARGSUSED*/ +static int +filt_ext2read(struct knote *kn, long hint) +{ + struct vnode *vp = (struct vnode *)kn->kn_hook; + struct inode *ip = VTOI(vp); + + /* + * filesystem is gone, so set the EOF flag and schedule + * the knote for deletion. + */ + if (hint == NOTE_REVOKE) { + kn->kn_flags |= (EV_EOF | EV_ONESHOT); + return (1); + } + + kn->kn_data = ip->i_size - kn->kn_fp->f_offset; + return (kn->kn_data != 0); +} + +/*ARGSUSED*/ +static int +filt_ext2write(struct knote *kn, long hint) +{ + /* + * filesystem is gone, so set the EOF flag and schedule + * the knote for deletion. + */ + if (hint == NOTE_REVOKE) + kn->kn_flags |= (EV_EOF | EV_ONESHOT); + + kn->kn_data = 0; + return (1); +} + +static int +filt_ext2vnode(struct knote *kn, long hint) +{ + if (kn->kn_sfflags & hint) + kn->kn_fflags |= hint; + if (hint == NOTE_REVOKE) { + kn->kn_flags |= EV_EOF; + return (1); + } + return (kn->kn_fflags != 0); +} + +static struct vop_ops *ext2_vnode_vops; +/* Global vfs data structures for ext2. */ +struct vnodeopv_entry_desc ext2_vnodeop_entries[] = { + { &vop_default_desc, vop_defaultop }, + { &vop_fsync_desc, (vnodeopv_entry_t) ext2_fsync }, + { &vop_read_desc, (vnodeopv_entry_t) ext2_read }, + { &vop_reallocblks_desc, (vnodeopv_entry_t) ext2_reallocblks }, + { &vop_write_desc, (vnodeopv_entry_t) ext2_write }, + { &vop_access_desc, (vnodeopv_entry_t) ext2_access }, + { &vop_advlock_desc, (vnodeopv_entry_t) ext2_advlock }, + { &vop_bmap_desc, (vnodeopv_entry_t) ext2_bmap }, + { &vop_old_lookup_desc, (vnodeopv_entry_t) ext2_lookup }, + { &vop_close_desc, (vnodeopv_entry_t) ext2_close }, + { &vop_old_create_desc, (vnodeopv_entry_t) ext2_create }, + { &vop_getattr_desc, (vnodeopv_entry_t) ext2_getattr }, + { &vop_inactive_desc, (vnodeopv_entry_t) ext2_inactive }, + { &vop_islocked_desc, (vnodeopv_entry_t) vop_stdislocked }, + { &vop_old_link_desc, (vnodeopv_entry_t) ext2_link }, + { &vop_lock_desc, (vnodeopv_entry_t) vop_stdlock }, + { &vop_old_mkdir_desc, (vnodeopv_entry_t) ext2_mkdir }, + { &vop_old_mknod_desc, (vnodeopv_entry_t) ext2_mknod }, + { &vop_mmap_desc, (vnodeopv_entry_t) ext2_mmap }, + { &vop_open_desc, (vnodeopv_entry_t) ext2_open }, + { &vop_pathconf_desc, (vnodeopv_entry_t) ext2_pathconf }, + { &vop_poll_desc, (vnodeopv_entry_t) vop_stdpoll }, + { &vop_kqfilter_desc, (vnodeopv_entry_t) ext2_kqfilter }, + { &vop_print_desc, (vnodeopv_entry_t) ext2_print }, + { &vop_readdir_desc, (vnodeopv_entry_t) ext2_readdir }, + { &vop_readlink_desc, (vnodeopv_entry_t) ext2_readlink }, + { &vop_reclaim_desc, (vnodeopv_entry_t) ext2_reclaim }, + { &vop_old_remove_desc, (vnodeopv_entry_t) ext2_remove }, + { &vop_old_rename_desc, (vnodeopv_entry_t) ext2_rename }, + { &vop_old_rmdir_desc, (vnodeopv_entry_t) ext2_rmdir }, + { &vop_setattr_desc, (vnodeopv_entry_t) ext2_setattr }, + { &vop_strategy_desc, (vnodeopv_entry_t) ext2_strategy }, + { &vop_old_symlink_desc, (vnodeopv_entry_t) ext2_symlink }, + { &vop_unlock_desc, (vnodeopv_entry_t) vop_stdunlock }, + { &vop_old_whiteout_desc, (vnodeopv_entry_t) ext2_whiteout }, + { &vop_getpages_desc, (vnodeopv_entry_t) ext2_getpages }, + { &vop_putpages_desc, (vnodeopv_entry_t) ext2_putpages }, + { NULL, NULL } +}; +static struct vnodeopv_desc ext2_vnodeop_opv_desc = + { &ext2_vnode_vops, ext2_vnodeop_entries, 0 }; + +static struct vop_ops *ext2_spec_vops; +struct vnodeopv_entry_desc ext2_specop_entries[] = { + { &vop_default_desc, (vnodeopv_entry_t) ext2_vnoperatespec }, + { &vop_fsync_desc, (vnodeopv_entry_t) ext2_fsync }, + { &vop_access_desc, (vnodeopv_entry_t) ext2_access }, + { &vop_close_desc, (vnodeopv_entry_t) ext2spec_close }, + { &vop_getattr_desc, (vnodeopv_entry_t) ext2_getattr }, + { &vop_inactive_desc, (vnodeopv_entry_t) ext2_inactive }, + { &vop_islocked_desc, (vnodeopv_entry_t) vop_stdislocked }, + { &vop_lock_desc, (vnodeopv_entry_t) vop_stdlock }, + { &vop_print_desc, (vnodeopv_entry_t) ext2_print }, + { &vop_read_desc, (vnodeopv_entry_t) ext2spec_read }, + { &vop_reclaim_desc, (vnodeopv_entry_t) ext2_reclaim }, + { &vop_setattr_desc, (vnodeopv_entry_t) ext2_setattr }, + { &vop_unlock_desc, (vnodeopv_entry_t) vop_stdunlock }, + { &vop_write_desc, (vnodeopv_entry_t) ext2spec_write }, + { NULL, NULL } +}; +static struct vnodeopv_desc ext2_specop_opv_desc = + { &ext2_spec_vops, ext2_specop_entries, 0 }; + +static struct vop_ops *ext2_fifo_vops; +struct vnodeopv_entry_desc ext2_fifoop_entries[] = { + { &vop_default_desc, (vnodeopv_entry_t) ext2_vnoperatefifo }, + { &vop_fsync_desc, (vnodeopv_entry_t) ext2_fsync }, + { &vop_access_desc, (vnodeopv_entry_t) ext2_access }, + { &vop_close_desc, (vnodeopv_entry_t) ext2fifo_close }, + { &vop_getattr_desc, (vnodeopv_entry_t) ext2_getattr }, + { &vop_inactive_desc, (vnodeopv_entry_t) ext2_inactive }, + { &vop_islocked_desc, (vnodeopv_entry_t) vop_stdislocked }, + { &vop_kqfilter_desc, (vnodeopv_entry_t) ext2fifo_kqfilter }, + { &vop_lock_desc, (vnodeopv_entry_t) vop_stdlock }, + { &vop_print_desc, (vnodeopv_entry_t) ext2_print }, + { &vop_read_desc, (vnodeopv_entry_t) ext2fifo_read }, + { &vop_reclaim_desc, (vnodeopv_entry_t) ext2_reclaim }, + { &vop_setattr_desc, (vnodeopv_entry_t) ext2_setattr }, + { &vop_unlock_desc, (vnodeopv_entry_t) vop_stdunlock }, + { &vop_write_desc, (vnodeopv_entry_t) ext2fifo_write }, + { NULL, NULL } +}; +static struct vnodeopv_desc ext2_fifoop_opv_desc = + { &ext2_fifo_vops, ext2_fifoop_entries, 0 }; + +VNODEOP_SET(ext2_vnodeop_opv_desc); +VNODEOP_SET(ext2_specop_opv_desc); +VNODEOP_SET(ext2_fifoop_opv_desc); + +/* + * ext2_vnoperate(struct vnodeop_desc *a_desc) + */ +int +ext2_vnoperate(struct vop_generic_args *ap) +{ + return (VOCALL(ext2_vnode_vops, ap)); +} + +/* + * ext2_vnoperatefifo(struct vnodeop_desc *a_desc) + */ +int +ext2_vnoperatefifo(struct vop_generic_args *ap) +{ + return (VOCALL(ext2_fifo_vops, ap)); +} + +/* + * ext2_vnoperatespec(struct vnodeop_desc *a_desc) + */ +int +ext2_vnoperatespec(struct vop_generic_args *ap) +{ + return (VOCALL(ext2_spec_vops, ap)); +} + diff --git a/sys/vfs/gnu/ext2fs/ext2mount.h b/sys/vfs/gnu/ext2fs/ext2mount.h new file mode 100644 index 0000000000..838337ddf3 --- /dev/null +++ b/sys/vfs/gnu/ext2fs/ext2mount.h @@ -0,0 +1,126 @@ +/*- + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. 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. + * + * @(#)ufsmount.h 8.6 (Berkeley) 3/30/95 + * $FreeBSD: src/sys/ufs/ufs/ufsmount.h,v 1.17 1999/12/29 04:55:06 peter Exp $ + * $DragonFly: src/sys/vfs/gnu/ext2fs/ext2mount.h,v 1.1 2006/04/04 17:34:32 dillon Exp $ + */ + +#ifndef _VFS_GNU_EXT2FS_EXT2MOUNT_H_ +#define _VFS_GNU_EXT2FS_EXT2MOUNT_H_ + +/* + * Arguments to mount UFS-based filesystems + */ +struct ext2_args { + char *fspec; /* block special device to mount */ + struct export_args export; /* network export information */ +}; + +/* + * Arguments to mount MFS + */ +struct mfs_args { + char *fspec; /* name to export for statfs */ + struct export_args export; /* if exported MFSes are supported */ + caddr_t base; /* base of filesystem in memory */ + u_long size; /* size of filesystem */ +}; + +#ifdef _KERNEL + +#ifdef MALLOC_DECLARE +MALLOC_DECLARE(M_EXT2MNT); +#endif + +struct buf; +struct inode; +struct timeval; +struct ucred; +struct uio; +struct vnode; +struct netexport; + +/* This structure describes the UFS specific mount structure data. */ +struct ext2mount { + struct mount *um_mountp; /* filesystem vfs structure */ + dev_t um_dev; /* device mounted */ + struct vnode *um_devvp; /* block device mounted vnode */ + + struct ext2_sb_info *um_e2fs; + struct vnode *um_quotas[MAXQUOTAS]; /* pointer to quota files */ + struct ucred *um_cred[MAXQUOTAS]; /* quota file access cred */ + u_long um_nindir; /* indirect ptrs per block */ + u_long um_bptrtodb; /* indir ptr to disk block */ + u_long um_seqinc; /* inc between seq blocks */ + time_t um_btime[MAXQUOTAS]; /* block quota time limit */ + time_t um_itime[MAXQUOTAS]; /* inode quota time limit */ + char um_qflags[MAXQUOTAS]; /* quota specific flags */ + struct netexport um_export; /* export information */ + int64_t um_savedmaxfilesize; /* XXX - limit maxfilesize */ + struct malloc_type *um_malloctype; /* The inodes malloctype */ + int um_i_effnlink_valid; /* i_effnlink valid? */ + int (*um_blkatoff) (struct vnode *, off_t, char **, struct buf **); + int (*um_truncate) (struct vnode *, off_t, int, struct ucred *, + struct thread *); + int (*um_update) (struct vnode *, int); + int (*um_valloc) (struct vnode *, int, struct ucred *, struct vnode **); + int (*um_vfree) (struct vnode *, ino_t, int); +}; + +#define um_e2fsb um_e2fs->s_es + +#define EXT2_BLKATOFF(aa, bb, cc, dd) VFSTOEXT2((aa)->v_mount)->um_blkatoff(aa, bb, cc, dd) +#define EXT2_TRUNCATE(aa, bb, cc, dd, ee) VFSTOEXT2((aa)->v_mount)->um_truncate(aa, bb, cc, dd, ee) +#define EXT2_UPDATE(aa, bb) VFSTOEXT2((aa)->v_mount)->um_update(aa, bb) +#define EXT2_VALLOC(aa, bb, cc, dd) VFSTOEXT2((aa)->v_mount)->um_valloc(aa, bb, cc, dd) +#define EXT2_VFREE(aa, bb, cc) VFSTOEXT2((aa)->v_mount)->um_vfree(aa, bb, cc) + +/* + * Flags describing the state of quotas. + */ +#define QTF_OPENING 0x01 /* Q_QUOTAON in progress */ +#define QTF_CLOSING 0x02 /* Q_QUOTAOFF in progress */ + +/* Convert mount ptr to ext2mount ptr. */ +#define VFSTOEXT2(mp) ((struct ext2mount *)((mp)->mnt_data)) + +/* + * Macros to access filesystem parameters in the ext2mount structure. + * Used by ufs_bmap. + */ +#define MNINDIR(ump) ((ump)->um_nindir) +#define blkptrtodb(ump, b) ((b) << (ump)->um_bptrtodb) +#define is_sequential(ump, a, b) ((b) == (a) + ump->um_seqinc) +#endif /* _KERNEL */ + +#endif /* !_VFS_GNU_EXT2FS_EXT2MOUNT_H_ */ diff --git a/sys/vfs/gnu/ext2fs/fs.h b/sys/vfs/gnu/ext2fs/fs.h index 611a53f42a..997591ff65 100644 --- a/sys/vfs/gnu/ext2fs/fs.h +++ b/sys/vfs/gnu/ext2fs/fs.h @@ -38,7 +38,7 @@ * * @(#)fs.h 8.7 (Berkeley) 4/19/94 * $FreeBSD: src/sys/gnu/ext2fs/fs.h,v 1.5.2.1 2000/11/11 13:12:45 bde Exp $ - * $DragonFly: src/sys/vfs/gnu/ext2fs/fs.h,v 1.7 2006/03/24 18:35:33 dillon Exp $ + * $DragonFly: src/sys/vfs/gnu/ext2fs/fs.h,v 1.8 2006/04/04 17:34:32 dillon Exp $ */ /* @@ -128,6 +128,8 @@ ((off_t)(blk) * (fs)->s_frag_size) #define dofftofsb(fs, blk) /* calculates blk / fs->fs_fsize */ \ ((daddr_t)((blk) / (fs)->s_frag_size)) +#define dbtodoff(fs, b) /* calculates diskblk * fs->fs_size */ \ + ((off_t)(b) << ((fs)->s_bshift - (fs)->s_fsbtodb)) #define lblkno(fs, loc) /* calculates (loc / fs->fs_bsize) */ \ ((loc) >> (fs->s_bshift)) @@ -164,7 +166,7 @@ extern u_char *fragtbl[]; * I haven't figured out yet what BSD does * I think I'll try a VOP_LOCK/VOP_UNLOCK on the device vnode */ -#define DEVVP(inode) (VFSTOUFS(ITOV(inode)->v_mount)->um_devvp) +#define DEVVP(inode) (VFSTOEXT2(ITOV(inode)->v_mount)->um_devvp) #define lock_super(devvp) vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, curthread) #define unlock_super(devvp) VOP_UNLOCK(devvp, 0, curthread) diff --git a/sys/vfs/gnu/ext2fs/i386-bitops.h b/sys/vfs/gnu/ext2fs/i386-bitops.h index fe699bb669..1bdea89bf6 100644 --- a/sys/vfs/gnu/ext2fs/i386-bitops.h +++ b/sys/vfs/gnu/ext2fs/i386-bitops.h @@ -1,13 +1,15 @@ -/* $FreeBSD: src/sys/gnu/ext2fs/i386-bitops.h,v 1.5 1999/11/15 23:16:06 obrien Exp $ */ -/* $DragonFly: src/sys/vfs/gnu/ext2fs/i386-bitops.h,v 1.3 2006/01/13 21:09:27 swildner Exp $ */ +/* + * $FreeBSD: src/sys/gnu/ext2fs/i386-bitops.h,v 1.5 1999/11/15 23:16:06 obrien Exp $ + * $DragonFly: src/sys/vfs/gnu/ext2fs/i386-bitops.h,v 1.4 2006/04/04 17:34:32 dillon Exp $ + */ /* * this is mixture of i386/bitops.h and asm/string.h * taken from the Linux source tree * * XXX replace with Mach routines or reprogram in C */ -#ifndef _SYS_GNU_EXT2FS_I386_BITOPS_H_ -#define _SYS_GNU_EXT2FS_I386_BITOPS_H_ +#ifndef _VFS_GNU_EXT2FS_I386_BITOPS_H_ +#define _VFS_GNU_EXT2FS_I386_BITOPS_H_ /* * Copyright 1992, Linus Torvalds. @@ -168,4 +170,4 @@ memscan(void *addr, unsigned char c, int size) return addr; } -#endif /* !_SYS_GNU_EXT2FS_I386_BITOPS_H_ */ +#endif /* _VFS_GNU_EXT2FS_I386_BITOPS_H_ */ diff --git a/sys/vfs/gnu/ext2fs/inode.h b/sys/vfs/gnu/ext2fs/inode.h new file mode 100644 index 0000000000..fe3d0c909c --- /dev/null +++ b/sys/vfs/gnu/ext2fs/inode.h @@ -0,0 +1,175 @@ +/*- + * Copyright (c) 1982, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. 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. + * + * @(#)inode.h 8.9 (Berkeley) 5/14/95 + * $FreeBSD: src/sys/ufs/ufs/inode.h,v 1.28.2.2 2001/09/29 12:52:52 iedowse Exp $ + * $DragonFly: src/sys/vfs/gnu/ext2fs/inode.h,v 1.1 2006/04/04 17:34:32 dillon Exp $ + */ + +#ifndef _VFS_GNU_EXT2FS_INODE_H_ +#define _VFS_GNU_EXT2FS_INODE_H_ + +#if defined(_KERNEL) || defined(_KERNEL_STRUCTURES) + +#ifndef _SYS_LOCK_H_ +#include +#endif +#ifndef _SYS_LOCKF_H +#include +#endif + +#endif + +#ifndef _SYS_QUEUE_H_ +#include +#endif +#include "dinode.h" + +/* + * The size of a logical block number. + */ +typedef long ext2_lbn_t; + +/* + * This must agree with the definition in "dir.h" + */ +#define doff_t int32_t + +#if defined(_KERNEL) || defined(_KERNEL_STRUCTURES) + +/* + * An EXT2FS inode is basically a UFS inode with some spare fields + * redefined and a different superblock structure. + */ +struct inode { + struct inode *i_next;/* Hash chain */ + struct vnode *i_vnode;/* Vnode associated with this inode. */ + struct vnode *i_devvp;/* Vnode for block I/O. */ + uint32_t i_flag; /* flags, see below */ + dev_t i_dev; /* Device associated with the inode. */ + ino_t i_number; /* The identity of the inode. */ + int i_effnlink; /* i_nlink when I/O completes */ + + struct ext2_sb_info *i_e2fs; /* EXT2FS */ + struct ext2_dquot *i_dquot[MAXQUOTAS]; /* Dquot structures. */ + u_quad_t i_modrev; /* Revision level for NFS lease. */ + struct lockf i_lockf;/* Head of byte-level lock list. */ + /* + * Side effects; used during directory lookup. + */ + int32_t i_count; /* Size of free slot in directory. */ + doff_t i_endoff; /* End of useful stuff in directory. */ + doff_t i_diroff; /* Offset in dir, where we found last entry. */ + doff_t i_offset; /* Offset of free space in directory. */ + ino_t i_ino; /* Inode number of found directory. */ + uint32_t i_reclen; /* Size of found directory entry. */ + uint32_t i_spare[3]; /* XXX actually non-spare (for ext2fs). */ + + struct dirhash *i_dirhash; /* Hashing for large directories */ + /* + * The on-disk dinode itself (128 bytes) + */ + struct ext2_dinode i_din; +}; + +#ifndef NO_I_DEFINES + +#define i_atime i_din.di_atime +#define i_atimensec i_din.di_atimensec +#define i_blocks i_din.di_blocks +#define i_ctime i_din.di_ctime +#define i_ctimensec i_din.di_ctimensec +#define i_db i_din.di_db +#define i_flags i_din.di_flags +#define i_gen i_din.di_gen +#define i_gid i_din.di_gid +#define i_ib i_din.di_ib +#define i_mode i_din.di_mode +#define i_mtime i_din.di_mtime +#define i_mtimensec i_din.di_mtimensec +#define i_nlink i_din.di_nlink +#define i_rdev i_din.di_rdev +#define i_shortlink i_din.di_shortlink +#define i_size i_din.di_size +#define i_uid i_din.di_uid +#define i_fsmid i_din.di_fsmid + +#endif + +#endif + +/* These flags are kept in i_flag. */ +#define IN_ACCESS 0x0001 /* Access time update request. */ +#define IN_CHANGE 0x0002 /* Inode change time update request. */ +#define IN_UPDATE 0x0004 /* Modification time update request. */ +#define IN_MODIFIED 0x0008 /* Inode has been modified. */ +#define IN_RENAME 0x0010 /* Inode is being renamed. */ +#define IN_SHLOCK 0x0020 /* File has shared lock. */ +#define IN_EXLOCK 0x0040 /* File has exclusive lock. */ +#define IN_HASHED 0x0080 /* Inode is on hash list */ +#define IN_LAZYMOD 0x0100 /* Modified, but don't write yet. */ + +#if defined(_KERNEL) || defined(_KERNEL_STRUCTURES) + +/* + * Structure used to pass around logical block paths generated by + * ext2_getlbns and used by truncate and bmap code. + */ +struct indir { + ext2_daddr_t in_lbn; /* Logical block number. */ + int in_off; /* Offset in buffer. */ + int in_exists; /* Flag if the block exists. */ +}; + +/* Convert between inode pointers and vnode pointers. */ +#define VTOI(vp) ((struct inode *)(vp)->v_data) +#define ITOV(ip) ((ip)->i_vnode) + +/* Determine if soft dependencies are being done */ +#define DOINGSOFTDEP(vp) ((vp)->v_mount->mnt_flag & MNT_SOFTDEP) +#define DOINGASYNC(vp) ((vp)->v_mount->mnt_flag & MNT_ASYNC) + +/* This overlays the fid structure (see mount.h). */ +struct ufid { + uint16_t ufid_len; /* Length of structure. */ + uint16_t ufid_pad; /* Force 32-bit alignment. */ + ino_t ufid_ino; /* File number (ino). */ + int32_t ufid_gen; /* Generation number. */ +}; +#endif /* _KERNEL || _KERNEL_STRUCTURES */ + +#endif /* !_VFS_GNU_EXT2FS_INODE_H_ */ diff --git a/sys/vfs/gnu/ext2fs/quota.h b/sys/vfs/gnu/ext2fs/quota.h new file mode 100644 index 0000000000..7e712765f3 --- /dev/null +++ b/sys/vfs/gnu/ext2fs/quota.h @@ -0,0 +1,205 @@ +/*- + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Robert Elz at The University of Melbourne. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. 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. + * + * @(#)quota.h 8.3 (Berkeley) 8/19/94 + * $FreeBSD: src/sys/ufs/ufs/quota.h,v 1.15.2.1 2003/02/27 12:04:13 das Exp $ + * $DragonFly: src/sys/vfs/gnu/ext2fs/quota.h,v 1.1 2006/04/04 17:34:32 dillon Exp $ + */ + +#ifndef _VFS_GNU_EXT2FS_QUOTA_H_ +#define _VFS_GNU_EXT2FS_QUOTA_H_ + +/* + * Definitions for disk quotas imposed on the average user + * (big brother finally hits UNIX). + * + * The following constants define the amount of time given a user before the + * soft limits are treated as hard limits (usually resulting in an allocation + * failure). The timer is started when the user crosses their soft limit, it + * is reset when they go below their soft limit. + */ +#define MAX_IQ_TIME (7*24*60*60) /* seconds in 1 week */ +#define MAX_DQ_TIME (7*24*60*60) /* seconds in 1 week */ + +/* + * The following constants define the usage of the quota file array in the + * ext2mount structure and dquot array in the inode structure. The semantics + * of the elements of these arrays are defined in the routine getinoquota; + * the remainder of the quota code treats them generically and need not be + * inspected when changing the size of the array. + */ +#define MAXQUOTAS 2 +#define USRQUOTA 0 /* element used for user quotas */ +#define GRPQUOTA 1 /* element used for group quotas */ + +/* + * Definitions for the default names of the quotas files. + */ +#define INITQFNAMES { \ + "user", /* USRQUOTA */ \ + "group", /* GRPQUOTA */ \ + "undefined", \ +} +#define QUOTAFILENAME "quota" +#define QUOTAGROUP "operator" + +/* + * Command definitions for the 'quotactl' system call. The commands are + * broken into a main command defined below and a subcommand that is used + * to convey the type of quota that is being manipulated (see above). + */ +#define SUBCMDMASK 0x00ff +#define SUBCMDSHIFT 8 +#define QCMD(cmd, type) (((cmd) << SUBCMDSHIFT) | ((type) & SUBCMDMASK)) + +#define Q_QUOTAON 0x0100 /* enable quotas */ +#define Q_QUOTAOFF 0x0200 /* disable quotas */ +#define Q_GETQUOTA 0x0300 /* get limits and usage */ +#define Q_SETQUOTA 0x0400 /* set limits and usage */ +#define Q_SETUSE 0x0500 /* set usage */ +#define Q_SYNC 0x0600 /* sync disk copy of a filesystems quotas */ + +/* + * The following structure defines the format of the disk quota file + * (as it appears on disk) - the file is an array of these structures + * indexed by user or group number. The setquota system call establishes + * the vnode for each quota file (a pointer is retained in the ext2mount + * structure). + */ +struct ext2_dqblk { + uint32_t dqb_bhardlimit; /* absolute limit on disk blks alloc */ + uint32_t dqb_bsoftlimit; /* preferred limit on disk blks */ + uint32_t dqb_curblocks; /* current block count */ + uint32_t dqb_ihardlimit; /* maximum # allocated inodes + 1 */ + uint32_t dqb_isoftlimit; /* preferred inode limit */ + uint32_t dqb_curinodes; /* current # allocated inodes */ + time_t dqb_btime; /* time limit for excessive disk use */ + time_t dqb_itime; /* time limit for excessive files */ +}; + +#ifdef _KERNEL + +#include + +/* + * The following structure records disk usage for a user or group on a + * filesystem. There is one allocated for each quota that exists on any + * filesystem for the current user or group. A cache is kept of recently + * used entries. + */ +struct ext2_dquot { + LIST_ENTRY(ext2_dquot) dq_hash; /* hash list */ + TAILQ_ENTRY(ext2_dquot) dq_freelist; /* free list */ + uint16_t dq_flags; /* flags, see below */ + uint16_t dq_type; /* quota type of this dquot */ + uint32_t dq_cnt; /* count of active references */ + uint32_t dq_id; /* identifier this applies to */ + struct ext2mount *dq_ump; /* filesystem that this is taken from */ + struct ext2_dqblk dq_dqb; /* actual usage & quotas */ +}; +/* + * Flag values. + */ +#define DQ_LOCK 0x01 /* this quota locked (no MODS) */ +#define DQ_WANT 0x02 /* wakeup on unlock */ +#define DQ_MOD 0x04 /* this quota modified since read */ +#define DQ_FAKE 0x08 /* no limits here, just usage */ +#define DQ_BLKS 0x10 /* has been warned about blk limit */ +#define DQ_INODS 0x20 /* has been warned about inode limit */ +/* + * Shorthand notation. + */ +#define dq_bhardlimit dq_dqb.dqb_bhardlimit +#define dq_bsoftlimit dq_dqb.dqb_bsoftlimit +#define dq_curblocks dq_dqb.dqb_curblocks +#define dq_ihardlimit dq_dqb.dqb_ihardlimit +#define dq_isoftlimit dq_dqb.dqb_isoftlimit +#define dq_curinodes dq_dqb.dqb_curinodes +#define dq_btime dq_dqb.dqb_btime +#define dq_itime dq_dqb.dqb_itime + +/* + * If the system has never checked for a quota for this file, then it is + * set to NODQUOT. Once a write attempt is made the inode pointer is set + * to reference a dquot structure. + */ +#define NODQUOT NULL + +/* + * Flags to ext2_chkdq() and ext2_chkiq() + */ +#define FORCE 0x01 /* force usage changes independent of limits */ +#define CHOWN 0x02 /* (advisory) change initiated by chown */ + +/* + * Macros to avoid subroutine calls to trivial functions. + */ +#ifdef DIAGNOSTIC +#define DQREF(dq) ext2_dqref(dq) +#else +#define DQREF(dq) (dq)->dq_cnt++ +#endif + +struct inode; +struct mount; +struct proc; +struct thread; +struct ucred; +struct vnode; + +int ext2_chkdq(struct inode *, long, struct ucred *, int); +int ext2_chkiq(struct inode *, long, struct ucred *, int); +void ext2_dqinit(void); +void ext2_dqrele(struct vnode *, struct ext2_dquot *); +int ext2_getinoquota(struct inode *); +int ext2_getquota(struct mount *, u_long, int, caddr_t); +int ext2_qsync(struct mount *mp); +int ext2_quotaoff(struct thread *, struct mount *, int); +int ext2_quotaon(struct thread *, struct mount *, int, caddr_t); +int ext2_setquota(struct mount *, u_long, int, caddr_t); +int ext2_setuse(struct mount *, u_long, int, caddr_t); +int ext2_quotactl(struct mount *, int, uid_t, caddr_t, struct thread *); + +#else /* !_KERNEL */ + +#include + +__BEGIN_DECLS +int quotactl(const char *, int, int, void *); +__END_DECLS + +#endif /* _KERNEL */ + +#endif /* !_VFS_GNU_EXT2FS_QUOTA_H_ */ -- 2.41.0