Browse Source

[PATCH] r/o bind mounts: monitor zeroing of i_nlink

Some filesystems, instead of simply decrementing i_nlink, simply zero it
during an unlink operation.  We need to catch these in addition to the
decrement operations.

Signed-off-by: Dave Hansen <haveblue@us.ibm.com>
Acked-by: Christoph Hellwig <hch@lst.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Dave Hansen 18 năm trước cách đây
mục cha
commit
ce71ec3684

+ 2 - 2
fs/autofs4/root.c

@@ -638,7 +638,7 @@ static int autofs4_dir_unlink(struct inode *dir, struct dentry *dentry)
 	dput(ino->dentry);
 	dput(ino->dentry);
 
 
 	dentry->d_inode->i_size = 0;
 	dentry->d_inode->i_size = 0;
-	dentry->d_inode->i_nlink = 0;
+	clear_nlink(dentry->d_inode);
 
 
 	dir->i_mtime = CURRENT_TIME;
 	dir->i_mtime = CURRENT_TIME;
 
 
@@ -673,7 +673,7 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry)
 	}
 	}
 	dput(ino->dentry);
 	dput(ino->dentry);
 	dentry->d_inode->i_size = 0;
 	dentry->d_inode->i_size = 0;
-	dentry->d_inode->i_nlink = 0;
+	clear_nlink(dentry->d_inode);
 
 
 	if (dir->i_nlink)
 	if (dir->i_nlink)
 		drop_nlink(dir);
 		drop_nlink(dir);

+ 1 - 1
fs/cifs/inode.c

@@ -818,7 +818,7 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
 	if (!rc) {
 	if (!rc) {
 		drop_nlink(inode);
 		drop_nlink(inode);
 		i_size_write(direntry->d_inode,0);
 		i_size_write(direntry->d_inode,0);
-		direntry->d_inode->i_nlink = 0;
+		clear_nlink(direntry->d_inode);
 	}
 	}
 
 
 	cifsInode = CIFS_I(direntry->d_inode);
 	cifsInode = CIFS_I(direntry->d_inode);

+ 1 - 1
fs/ext3/namei.c

@@ -2045,7 +2045,7 @@ static int ext3_rmdir (struct inode * dir, struct dentry *dentry)
 			      "empty directory has nlink!=2 (%d)",
 			      "empty directory has nlink!=2 (%d)",
 			      inode->i_nlink);
 			      inode->i_nlink);
 	inode->i_version++;
 	inode->i_version++;
-	inode->i_nlink = 0;
+	clear_nlink(inode);
 	/* There's no need to set i_disksize: the fact that i_nlink is
 	/* There's no need to set i_disksize: the fact that i_nlink is
 	 * zero will ensure that the right thing happens during any
 	 * zero will ensure that the right thing happens during any
 	 * recovery. */
 	 * recovery. */

+ 2 - 2
fs/fuse/dir.c

@@ -508,7 +508,7 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
 		/* Set nlink to zero so the inode can be cleared, if
 		/* Set nlink to zero so the inode can be cleared, if
                    the inode does have more links this will be
                    the inode does have more links this will be
                    discovered at the next lookup/getattr */
                    discovered at the next lookup/getattr */
-		inode->i_nlink = 0;
+		clear_nlink(inode);
 		fuse_invalidate_attr(inode);
 		fuse_invalidate_attr(inode);
 		fuse_invalidate_attr(dir);
 		fuse_invalidate_attr(dir);
 		fuse_invalidate_entry_cache(entry);
 		fuse_invalidate_entry_cache(entry);
@@ -534,7 +534,7 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry)
 	err = req->out.h.error;
 	err = req->out.h.error;
 	fuse_put_request(fc, req);
 	fuse_put_request(fc, req);
 	if (!err) {
 	if (!err) {
-		entry->d_inode->i_nlink = 0;
+		clear_nlink(entry->d_inode);
 		fuse_invalidate_attr(dir);
 		fuse_invalidate_attr(dir);
 		fuse_invalidate_entry_cache(entry);
 		fuse_invalidate_entry_cache(entry);
 	} else if (err == -EINTR)
 	} else if (err == -EINTR)

+ 1 - 1
fs/hfs/dir.c

@@ -273,7 +273,7 @@ static int hfs_rmdir(struct inode *dir, struct dentry *dentry)
 	res = hfs_cat_delete(inode->i_ino, dir, &dentry->d_name);
 	res = hfs_cat_delete(inode->i_ino, dir, &dentry->d_name);
 	if (res)
 	if (res)
 		return res;
 		return res;
-	inode->i_nlink = 0;
+	clear_nlink(inode);
 	inode->i_ctime = CURRENT_TIME_SEC;
 	inode->i_ctime = CURRENT_TIME_SEC;
 	hfs_delete_inode(inode);
 	hfs_delete_inode(inode);
 	mark_inode_dirty(inode);
 	mark_inode_dirty(inode);

+ 2 - 2
fs/hfsplus/dir.c

@@ -348,7 +348,7 @@ static int hfsplus_unlink(struct inode *dir, struct dentry *dentry)
 		} else
 		} else
 			inode->i_flags |= S_DEAD;
 			inode->i_flags |= S_DEAD;
 	} else
 	} else
-		inode->i_nlink = 0;
+		clear_nlink(inode);
 	inode->i_ctime = CURRENT_TIME_SEC;
 	inode->i_ctime = CURRENT_TIME_SEC;
 	mark_inode_dirty(inode);
 	mark_inode_dirty(inode);
 
 
@@ -387,7 +387,7 @@ static int hfsplus_rmdir(struct inode *dir, struct dentry *dentry)
 	res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
 	res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
 	if (res)
 	if (res)
 		return res;
 		return res;
-	inode->i_nlink = 0;
+	clear_nlink(inode);
 	inode->i_ctime = CURRENT_TIME_SEC;
 	inode->i_ctime = CURRENT_TIME_SEC;
 	hfsplus_delete_inode(inode);
 	hfsplus_delete_inode(inode);
 	mark_inode_dirty(inode);
 	mark_inode_dirty(inode);

+ 2 - 2
fs/hpfs/namei.c

@@ -495,7 +495,7 @@ static int hpfs_rmdir(struct inode *dir, struct dentry *dentry)
 		break;
 		break;
 	default:
 	default:
 		drop_nlink(dir);
 		drop_nlink(dir);
-		inode->i_nlink = 0;
+		clear_nlink(inode);
 		err = 0;
 		err = 0;
 	}
 	}
 	goto out;
 	goto out;
@@ -590,7 +590,7 @@ static int hpfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 		int r;
 		int r;
 		if ((r = hpfs_remove_dirent(old_dir, dno, dep, &qbh, 1)) != 2) {
 		if ((r = hpfs_remove_dirent(old_dir, dno, dep, &qbh, 1)) != 2) {
 			if ((nde = map_dirent(new_dir, hpfs_i(new_dir)->i_dno, (char *)new_name, new_len, NULL, &qbh1))) {
 			if ((nde = map_dirent(new_dir, hpfs_i(new_dir)->i_dno, (char *)new_name, new_len, NULL, &qbh1))) {
-				new_inode->i_nlink = 0;
+				clear_nlink(new_inode);
 				copy_de(nde, &de);
 				copy_de(nde, &de);
 				memcpy(nde->name, new_name, new_len);
 				memcpy(nde->name, new_name, new_len);
 				hpfs_mark_4buffers_dirty(&qbh1);
 				hpfs_mark_4buffers_dirty(&qbh1);

+ 1 - 1
fs/jfs/namei.c

@@ -414,7 +414,7 @@ static int jfs_rmdir(struct inode *dip, struct dentry *dentry)
 	JFS_IP(ip)->acl.flag = 0;
 	JFS_IP(ip)->acl.flag = 0;
 
 
 	/* mark the target directory as deleted */
 	/* mark the target directory as deleted */
-	ip->i_nlink = 0;
+	clear_nlink(ip);
 	mark_inode_dirty(ip);
 	mark_inode_dirty(ip);
 
 
 	rc = txCommit(tid, 2, &iplist[0], 0);
 	rc = txCommit(tid, 2, &iplist[0], 0);

+ 2 - 2
fs/msdos/namei.c

@@ -345,7 +345,7 @@ static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
 		goto out;
 		goto out;
 	drop_nlink(dir);
 	drop_nlink(dir);
 
 
-	inode->i_nlink = 0;
+	clear_nlink(inode);
 	inode->i_ctime = CURRENT_TIME_SEC;
 	inode->i_ctime = CURRENT_TIME_SEC;
 	fat_detach(inode);
 	fat_detach(inode);
 out:
 out:
@@ -430,7 +430,7 @@ static int msdos_unlink(struct inode *dir, struct dentry *dentry)
 	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
 	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
 	if (err)
 	if (err)
 		goto out;
 		goto out;
-	inode->i_nlink = 0;
+	clear_nlink(inode);
 	inode->i_ctime = CURRENT_TIME_SEC;
 	inode->i_ctime = CURRENT_TIME_SEC;
 	fat_detach(inode);
 	fat_detach(inode);
 out:
 out:

+ 1 - 1
fs/nfs/dir.c

@@ -1286,7 +1286,7 @@ static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
 	error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name);
 	error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name);
 	/* Ensure the VFS deletes this inode */
 	/* Ensure the VFS deletes this inode */
 	if (error == 0 && dentry->d_inode != NULL)
 	if (error == 0 && dentry->d_inode != NULL)
-		dentry->d_inode->i_nlink = 0;
+		clear_nlink(dentry->d_inode);
 	nfs_end_data_update(dir);
 	nfs_end_data_update(dir);
 	unlock_kernel();
 	unlock_kernel();
 
 

+ 1 - 1
fs/qnx4/namei.c

@@ -186,7 +186,7 @@ int qnx4_rmdir(struct inode *dir, struct dentry *dentry)
 	memset(de->di_fname, 0, sizeof de->di_fname);
 	memset(de->di_fname, 0, sizeof de->di_fname);
 	de->di_mode = 0;
 	de->di_mode = 0;
 	mark_buffer_dirty(bh);
 	mark_buffer_dirty(bh);
-	inode->i_nlink = 0;
+	clear_nlink(inode);
 	mark_inode_dirty(inode);
 	mark_inode_dirty(inode);
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
 	inode_dec_link_count(dir);
 	inode_dec_link_count(dir);

+ 2 - 2
fs/reiserfs/namei.c

@@ -913,7 +913,7 @@ static int reiserfs_rmdir(struct inode *dir, struct dentry *dentry)
 		reiserfs_warning(inode->i_sb, "%s: empty directory has nlink "
 		reiserfs_warning(inode->i_sb, "%s: empty directory has nlink "
 				 "!= 2 (%d)", __FUNCTION__, inode->i_nlink);
 				 "!= 2 (%d)", __FUNCTION__, inode->i_nlink);
 
 
-	inode->i_nlink = 0;
+	clear_nlink(inode);
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
 	reiserfs_update_sd(&th, inode);
 	reiserfs_update_sd(&th, inode);
 
 
@@ -1473,7 +1473,7 @@ static int reiserfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 	if (new_dentry_inode) {
 	if (new_dentry_inode) {
 		// adjust link number of the victim
 		// adjust link number of the victim
 		if (S_ISDIR(new_dentry_inode->i_mode)) {
 		if (S_ISDIR(new_dentry_inode->i_mode)) {
-			new_dentry_inode->i_nlink = 0;
+			clear_nlink(new_dentry_inode);
 		} else {
 		} else {
 			drop_nlink(new_dentry_inode);
 			drop_nlink(new_dentry_inode);
 		}
 		}

+ 1 - 1
fs/udf/namei.c

@@ -876,7 +876,7 @@ static int udf_rmdir(struct inode * dir, struct dentry * dentry)
 		udf_warning(inode->i_sb, "udf_rmdir",
 		udf_warning(inode->i_sb, "udf_rmdir",
 			"empty directory has nlink != 2 (%d)",
 			"empty directory has nlink != 2 (%d)",
 			inode->i_nlink);
 			inode->i_nlink);
-	inode->i_nlink = 0;
+	clear_nlink(inode);
 	inode->i_size = 0;
 	inode->i_size = 0;
 	inode_dec_link_count(inode);
 	inode_dec_link_count(inode);
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = current_fs_time(dir->i_sb);
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = current_fs_time(dir->i_sb);

+ 2 - 2
fs/vfat/namei.c

@@ -784,7 +784,7 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry)
 		goto out;
 		goto out;
 	drop_nlink(dir);
 	drop_nlink(dir);
 
 
-	inode->i_nlink = 0;
+	clear_nlink(inode);
 	inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
 	inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
 	fat_detach(inode);
 	fat_detach(inode);
 out:
 out:
@@ -808,7 +808,7 @@ static int vfat_unlink(struct inode *dir, struct dentry *dentry)
 	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
 	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
 	if (err)
 	if (err)
 		goto out;
 		goto out;
-	inode->i_nlink = 0;
+	clear_nlink(inode);
 	inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
 	inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
 	fat_detach(inode);
 	fat_detach(inode);
 out:
 out:

+ 5 - 0
include/linux/fs.h

@@ -1235,6 +1235,11 @@ static inline void drop_nlink(struct inode *inode)
 	inode->i_nlink--;
 	inode->i_nlink--;
 }
 }
 
 
+static inline void clear_nlink(struct inode *inode)
+{
+	inode->i_nlink = 0;
+}
+
 static inline void inode_dec_link_count(struct inode *inode)
 static inline void inode_dec_link_count(struct inode *inode)
 {
 {
 	drop_nlink(inode);
 	drop_nlink(inode);