svn commit: r320211 - head/sys/fs/ext2fs

Pedro F. Giffuni pfg at FreeBSD.org
Thu Jun 22 02:43:34 UTC 2017


Author: pfg
Date: Thu Jun 22 02:43:32 2017
New Revision: 320211
URL: https://svnweb.freebsd.org/changeset/base/320211

Log:
  ext2fs: add dir_nlink feature support.
  
  ext4 on linux has always supported more than 32000 directories through
  the dir_nlink feature, but FreeBSD was unable to catch up on this feature.
  As part of the 64 bit inode changes nlink_t has been extended and this
  feature is now possible.
  
  Submitted by:	Fedor Uporov
  Differential Revision:	https://reviews.freebsd.org/D11210

Modified:
  head/sys/fs/ext2fs/ext2_dir.h
  head/sys/fs/ext2fs/ext2_vnops.c
  head/sys/fs/ext2fs/ext2fs.h
  head/sys/fs/ext2fs/inode.h

Modified: head/sys/fs/ext2fs/ext2_dir.h
==============================================================================
--- head/sys/fs/ext2fs/ext2_dir.h	Thu Jun 22 02:19:39 2017	(r320210)
+++ head/sys/fs/ext2fs/ext2_dir.h	Thu Jun 22 02:43:32 2017	(r320211)
@@ -74,6 +74,7 @@ struct ext2fs_direct_2 {
  * Maximal count of links to a file
  */
 #define	EXT2_LINK_MAX	32000
+#define	EXT4_LINK_MAX	65000
 
 /*
  * Ext2 directory file types.  Only the low 3 bits are used.  The

Modified: head/sys/fs/ext2fs/ext2_vnops.c
==============================================================================
--- head/sys/fs/ext2fs/ext2_vnops.c	Thu Jun 22 02:19:39 2017	(r320210)
+++ head/sys/fs/ext2fs/ext2_vnops.c	Thu Jun 22 02:43:32 2017	(r320211)
@@ -672,6 +672,19 @@ out:
 	return (error);
 }
 
+static unsigned short
+ext2_max_nlink(struct inode *ip)
+{
+	struct m_ext2fs *fs;
+
+	fs = ip->i_e2fs;
+
+	if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_DIR_NLINK))
+		return (EXT4_LINK_MAX);
+	else
+		return (EXT2_LINK_MAX);
+}
+
 /*
  * link vnode call
  */
@@ -689,7 +702,7 @@ ext2_link(struct vop_link_args *ap)
 		panic("ext2_link: no name");
 #endif
 	ip = VTOI(vp);
-	if ((nlink_t)ip->i_nlink >= EXT2_LINK_MAX) {
+	if ((nlink_t)ip->i_nlink >= ext2_max_nlink(ip)) {
 		error = EMLINK;
 		goto out;
 	}
@@ -710,6 +723,31 @@ out:
 	return (error);
 }
 
+static int
+ext2_inc_nlink(struct inode *ip)
+{
+
+	ip->i_nlink++;
+
+	if (ext2_htree_has_idx(ip) && ip->i_nlink > 1) {
+		if (ip->i_nlink >= ext2_max_nlink(ip) || ip->i_nlink == 2)
+			ip->i_nlink = 1;
+	} else if (ip->i_nlink > ext2_max_nlink(ip)) {
+		ip->i_nlink--;
+		return (EMLINK);
+	}
+
+	return (0);
+}
+
+static void
+ext2_dec_nlink(struct inode *ip)
+{
+
+	if (!S_ISDIR(ip->i_mode) || ip->i_nlink > 2)
+		ip->i_nlink--;
+}
+
 /*
  * Rename system call.
  * 	rename("foo", "bar");
@@ -792,7 +830,7 @@ abortit:
 		goto abortit;
 	dp = VTOI(fdvp);
 	ip = VTOI(fvp);
-	if (ip->i_nlink >= EXT2_LINK_MAX) {
+	if (ip->i_nlink >= ext2_max_nlink(ip) && !ext2_htree_has_idx(ip)) {
 		VOP_UNLOCK(fvp, 0);
 		error = EMLINK;
 		goto abortit;
@@ -835,7 +873,7 @@ abortit:
 	 *    completing our work, the link count
 	 *    may be wrong, but correctable.
 	 */
-	ip->i_nlink++;
+	ext2_inc_nlink(ip);
 	ip->i_flag |= IN_CHANGE;
 	if ((error = ext2_update(fvp, !DOINGASYNC(fvp))) != 0) {
 		VOP_UNLOCK(fvp, 0);
@@ -890,11 +928,10 @@ abortit:
 		 * parent we don't fool with the link count.
 		 */
 		if (doingdirectory && newparent) {
-			if ((nlink_t)dp->i_nlink >= EXT2_LINK_MAX) {
-				error = EMLINK;
+			error = ext2_inc_nlink(dp);
+			if (error)
 				goto bad;
-			}
-			dp->i_nlink++;
+
 			dp->i_flag |= IN_CHANGE;
 			error = ext2_update(tdvp, !DOINGASYNC(tdvp));
 			if (error)
@@ -903,7 +940,7 @@ abortit:
 		error = ext2_direnter(ip, tdvp, tcnp);
 		if (error) {
 			if (doingdirectory && newparent) {
-				dp->i_nlink--;
+				ext2_dec_nlink(dp);
 				dp->i_flag |= IN_CHANGE;
 				(void)ext2_update(tdvp, 1);
 			}
@@ -936,8 +973,7 @@ abortit:
 		 * (both directories, or both not directories).
 		 */
 		if ((xp->i_mode & IFMT) == IFDIR) {
-			if (!ext2_dirempty(xp, dp->i_number, tcnp->cn_cred) ||
-			    xp->i_nlink > 2) {
+			if (!ext2_dirempty(xp, dp->i_number, tcnp->cn_cred)) {
 				error = ENOTEMPTY;
 				goto bad;
 			}
@@ -960,7 +996,7 @@ abortit:
 		 * of the target directory.
 		 */
 		if (doingdirectory && !newparent) {
-			dp->i_nlink--;
+			ext2_dec_nlink(dp);
 			dp->i_flag |= IN_CHANGE;
 		}
 		vput(tdvp);
@@ -974,7 +1010,7 @@ abortit:
 		 * it above, as the remaining link would point to
 		 * a directory without "." or ".." entries.
 		 */
-		xp->i_nlink--;
+		ext2_dec_nlink(xp);
 		if (doingdirectory) {
 			if (--xp->i_nlink != 0)
 				panic("ext2_rename: linked directory");
@@ -1031,7 +1067,7 @@ abortit:
 		 * and ".." set to point to the new parent.
 		 */
 		if (doingdirectory && newparent) {
-			dp->i_nlink--;
+			ext2_dec_nlink(dp);
 			dp->i_flag |= IN_CHANGE;
 			error = vn_rdwr(UIO_READ, fvp, (caddr_t)&dirbuf,
 			    sizeof(struct dirtemplate), (off_t)0,
@@ -1060,7 +1096,7 @@ abortit:
 		}
 		error = ext2_dirremove(fdvp, fcnp);
 		if (!error) {
-			xp->i_nlink--;
+			ext2_dec_nlink(xp);
 			xp->i_flag |= IN_CHANGE;
 		}
 		xp->i_flag &= ~IN_RENAME;
@@ -1080,7 +1116,7 @@ out:
 	if (doingdirectory)
 		ip->i_flag &= ~IN_RENAME;
 	if (vn_lock(fvp, LK_EXCLUSIVE) == 0) {
-		ip->i_nlink--;
+		ext2_dec_nlink(ip);
 		ip->i_flag |= IN_CHANGE;
 		ip->i_flag &= ~IN_RENAME;
 		vput(fvp);
@@ -1255,7 +1291,8 @@ ext2_mkdir(struct vop_mkdir_args *ap)
 		panic("ext2_mkdir: no name");
 #endif
 	dp = VTOI(dvp);
-	if ((nlink_t)dp->i_nlink >= EXT2_LINK_MAX) {
+	if ((nlink_t)dp->i_nlink >= ext2_max_nlink(dp) &&
+	    !ext2_htree_has_idx(dp)) {
 		error = EMLINK;
 		goto out;
 	}
@@ -1306,7 +1343,7 @@ ext2_mkdir(struct vop_mkdir_args *ap)
 	 * be done before reference is created
 	 * so reparation is possible if we crash.
 	 */
-	dp->i_nlink++;
+	ext2_inc_nlink(dp);
 	dp->i_flag |= IN_CHANGE;
 	error = ext2_update(dvp, !DOINGASYNC(dvp));
 	if (error)
@@ -1333,7 +1370,7 @@ ext2_mkdir(struct vop_mkdir_args *ap)
 	    IO_NODELOCKED | IO_SYNC | IO_NOMACCHECK, cnp->cn_cred, NOCRED,
 	    NULL, NULL);
 	if (error) {
-		dp->i_nlink--;
+		ext2_dec_nlink(dp);
 		dp->i_flag |= IN_CHANGE;
 		goto bad;
 	}
@@ -1358,7 +1395,7 @@ ext2_mkdir(struct vop_mkdir_args *ap)
 	/* Directory set up, now install its entry in the parent directory. */
 	error = ext2_direnter(ip, dvp, cnp);
 	if (error) {
-		dp->i_nlink--;
+		ext2_dec_nlink(dp);
 		dp->i_flag |= IN_CHANGE;
 	}
 bad:
@@ -1400,7 +1437,7 @@ ext2_rmdir(struct vop_rmdir_args *ap)
 	 *  the current directory and thus be
 	 *  non-empty.)
 	 */
-	if (ip->i_nlink != 2 || !ext2_dirempty(ip, dp->i_number, cnp->cn_cred)) {
+	if (!ext2_dirempty(ip, dp->i_number, cnp->cn_cred)) {
 		error = ENOTEMPTY;
 		goto out;
 	}
@@ -1417,22 +1454,15 @@ ext2_rmdir(struct vop_rmdir_args *ap)
 	error = ext2_dirremove(dvp, cnp);
 	if (error)
 		goto out;
-	dp->i_nlink--;
+	ext2_dec_nlink(dp);
 	dp->i_flag |= IN_CHANGE;
 	cache_purge(dvp);
 	VOP_UNLOCK(dvp, 0);
 	/*
 	 * Truncate inode.  The only stuff left
-	 * in the directory is "." and "..".  The
-	 * "." reference is inconsequential since
-	 * we're quashing it.  The ".." reference
-	 * has already been adjusted above.  We've
-	 * removed the "." reference and the reference
-	 * in the parent directory, but there may be
-	 * other hard links so decrement by 2 and
-	 * worry about them later.
+	 * in the directory is "." and "..".
 	 */
-	ip->i_nlink -= 2;
+	ip->i_nlink = 0;
 	error = ext2_truncate(vp, (off_t)0, IO_SYNC, cnp->cn_cred,
 	    cnp->cn_thread);
 	cache_purge(ITOV(ip));
@@ -1592,7 +1622,10 @@ ext2_pathconf(struct vop_pathconf_args *ap)
 
 	switch (ap->a_name) {
 	case _PC_LINK_MAX:
-		*ap->a_retval = EXT2_LINK_MAX;
+		if (ext2_htree_has_idx(VTOI(ap->a_vp)))
+			*ap->a_retval = INT_MAX;
+		else
+			*ap->a_retval = ext2_max_nlink(VTOI(ap->a_vp));
 		break;
 	case _PC_NAME_MAX:
 		*ap->a_retval = NAME_MAX;

Modified: head/sys/fs/ext2fs/ext2fs.h
==============================================================================
--- head/sys/fs/ext2fs/ext2fs.h	Thu Jun 22 02:19:39 2017	(r320210)
+++ head/sys/fs/ext2fs/ext2fs.h	Thu Jun 22 02:43:32 2017	(r320211)
@@ -260,6 +260,7 @@ struct csum {
 #define	EXT2F_ROCOMPAT_SUPP		(EXT2F_ROCOMPAT_SPARSESUPER | \
 					 EXT2F_ROCOMPAT_LARGEFILE | \
 					 EXT2F_ROCOMPAT_GDT_CSUM | \
+					 EXT2F_ROCOMPAT_DIR_NLINK | \
 					 EXT2F_ROCOMPAT_HUGE_FILE | \
 					 EXT2F_ROCOMPAT_EXTRA_ISIZE)
 #define	EXT2F_INCOMPAT_SUPP		EXT2F_INCOMPAT_FTYPE

Modified: head/sys/fs/ext2fs/inode.h
==============================================================================
--- head/sys/fs/ext2fs/inode.h	Thu Jun 22 02:19:39 2017	(r320210)
+++ head/sys/fs/ext2fs/inode.h	Thu Jun 22 02:43:32 2017	(r320211)
@@ -91,7 +91,7 @@ struct inode {
 
 	/* Fields from struct dinode in UFS. */
 	uint16_t	i_mode;		/* IFMT, permissions; see below. */
-	int16_t		i_nlink;	/* File link count. */
+	int32_t		i_nlink;	/* File link count. */
 	uint32_t	i_uid;		/* File owner. */
 	uint32_t	i_gid;		/* File group. */
 	uint64_t	i_size;		/* File byte count. */


More information about the svn-src-head mailing list