what is necessary for directory hard links

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch is the sum total of all that I had to change in the kernel
VFS layer to support hard links to directories and a few worse things
(hard links to things that are sometimes directories). Never mind
about do_path_lookup. I call that from an ioctl (flink).

Note the missing signed-off line. Not ready for mainline. Not adequitely tested.
Also understand that a filesystem cannot just set the flags and expect
hard links to work for it. There will be races: the target directory
for any operation is locked, but the item is not.
Oh, and any user will have to implement d_revalidate.

If you say this shouldn't be done maybe you're right but I'm doing it
for myself anyway.
If you have constructive feedback I will listen. If you want this in
kernel, you better have
a worthy user for it and be ready to hammer it. Tested with light load
on one filesystem on one architecture with one set of configuration
options doesn't quailfy. I haven't even passed it through the lock
validator yet because the backing filesystem isn't ready.

---
diff -rup -X linux-2.6.18-rc2/Documentation/dontdiff
linux-2.6.18-rc2/fs/namei.c linux-2.6.18-rc2-kb0/fs/namei.c
--- linux-2.6.18-rc2/fs/namei.c	2006-07-17 18:53:13.000000000 -0700
+++ linux-2.6.18-rc2-kb0/fs/namei.c	2006-07-17 17:35:43.000000000 -0700
@@ -1069,7 +1069,7 @@ set_it:
}

/* Returns 0 and nd will be valid on success; Retuns error, otherwise. */
-static int fastcall do_path_lookup(int dfd, const char *name,
+int fastcall do_path_lookup(int dfd, const char *name,
				unsigned int flags, struct nameidata *nd)
{
	int retval = 0;
@@ -1416,13 +1416,22 @@ static inline int lookup_flags(unsigned
}

/*
+ * Return true if assumptions in Documentation/filesystems/directory-locking
+ * are false [fs must set]. FS must have replacement locking in itself.
+ */
+static inline int fs_is_insane(struct inode *inode)
+{
+	return inode->i_sb->s_type->fs_flags & FS_OWN_LOCKS;
+}
+
+/*
 * p1 and p2 should be directories on the same fs.
 */
struct dentry *lock_rename(struct dentry *p1, struct dentry *p2)
{
	struct dentry *p;

-	if (p1 == p2) {
+	if (p1->d_inode == p2->d_inode) {
		mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT);
		return NULL;
	}
@@ -1453,7 +1462,7 @@ struct dentry *lock_rename(struct dentry
void unlock_rename(struct dentry *p1, struct dentry *p2)
{
	mutex_unlock(&p1->d_inode->i_mutex);
-	if (p1 != p2) {
+	if (p1->d_inode != p2->d_inode) {
		mutex_unlock(&p2->d_inode->i_mutex);
		mutex_unlock(&p1->d_inode->i_sb->s_vfs_rename_mutex);
	}
@@ -1967,7 +1976,8 @@ int vfs_rmdir(struct inode *dir, struct

	DQUOT_INIT(dir);

-	mutex_lock(&dentry->d_inode->i_mutex);
+	if (!(unlikely(fs_is_insane(dentry->d_inode))))
+		mutex_lock(&dentry->d_inode->i_mutex);
	dentry_unhash(dentry);
	if (d_mountpoint(dentry))
		error = -EBUSY;
@@ -1975,11 +1985,12 @@ int vfs_rmdir(struct inode *dir, struct
		error = security_inode_rmdir(dir, dentry);
		if (!error) {
			error = dir->i_op->rmdir(dir, dentry);
-			if (!error)
+			if (!error && likely(dentry->d_inode->i_nlink == 0))
				dentry->d_inode->i_flags |= S_DEAD;
		}
	}
-	mutex_unlock(&dentry->d_inode->i_mutex);
+	if (!(unlikely(fs_is_insane(dentry->d_inode))))
+		mutex_unlock(&dentry->d_inode->i_mutex);
	if (!error) {
		d_delete(dentry);
	}
@@ -2046,7 +2057,8 @@ int vfs_unlink(struct inode *dir, struct

	DQUOT_INIT(dir);

-	mutex_lock(&dentry->d_inode->i_mutex);
+	if (!(unlikely(fs_is_insane(dentry->d_inode))))
+		mutex_lock(&dentry->d_inode->i_mutex);
	if (d_mountpoint(dentry))
		error = -EBUSY;
	else {
@@ -2054,7 +2066,8 @@ int vfs_unlink(struct inode *dir, struct
		if (!error)
			error = dir->i_op->unlink(dir, dentry);
	}
-	mutex_unlock(&dentry->d_inode->i_mutex);
+	if (!(unlikely(fs_is_insane(dentry->d_inode))))
+		mutex_unlock(&dentry->d_inode->i_mutex);

	/* We don't d_delete() NFS sillyrenamed files--they still exist. */
	if (!error && !(dentry->d_flags & DCACHE_NFSFS_RENAMED)) {
@@ -2216,16 +2229,20 @@ int vfs_link(struct dentry *old_dentry,
	if (!dir->i_op || !dir->i_op->link)
		return -EPERM;
	if (S_ISDIR(old_dentry->d_inode->i_mode))
-		return -EPERM;
+		if (!old_dentry->d_inode->i_sb->s_type->fs_flags
+					& FS_ALLOW_HLINK_DIR)
+			return -EPERM;

	error = security_inode_link(old_dentry, dir, new_dentry);
	if (error)
		return error;

-	mutex_lock(&old_dentry->d_inode->i_mutex);
+	if (!(unlikely(fs_is_insane(old_dentry->d_inode))))
+		mutex_lock(&old_dentry->d_inode->i_mutex);
	DQUOT_INIT(dir);
	error = dir->i_op->link(old_dentry, dir, new_dentry);
-	mutex_unlock(&old_dentry->d_inode->i_mutex);
+	if (!(unlikely(fs_is_insane(old_dentry->d_inode))))
+		mutex_unlock(&old_dentry->d_inode->i_mutex);
	if (!error)
		fsnotify_create(dir, new_dentry);
	return error;
@@ -2343,7 +2360,8 @@ static int vfs_rename_dir(struct inode *

	target = new_dentry->d_inode;
	if (target) {
-		mutex_lock(&target->i_mutex);
+		if (!(unlikely(fs_is_insane(target))))
+			mutex_lock(&target->i_mutex);
		dentry_unhash(new_dentry);
	}
	if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
@@ -2353,7 +2371,8 @@ static int vfs_rename_dir(struct inode *
	if (target) {
		if (!error)
			target->i_flags |= S_DEAD;
-		mutex_unlock(&target->i_mutex);
+		if (!(unlikely(fs_is_insane(target))))
+			mutex_unlock(&target->i_mutex);
		if (d_unhashed(new_dentry))
			d_rehash(new_dentry);
		dput(new_dentry);
@@ -2375,7 +2394,7 @@ static int vfs_rename_other(struct inode

	dget(new_dentry);
	target = new_dentry->d_inode;
-	if (target)
+	if (target && !(unlikely(fs_is_insane(target))))
		mutex_lock(&target->i_mutex);
	if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
		error = -EBUSY;
@@ -2386,7 +2405,7 @@ static int vfs_rename_other(struct inode
		if (!(old_dir->i_sb->s_type->fs_flags & FS_ODD_RENAME))
			d_move(old_dentry, new_dentry);
	}
-	if (target)
+	if (target && !(unlikely(fs_is_insane(target))))
		mutex_unlock(&target->i_mutex);
	dput(new_dentry);
	return error;
@@ -2713,6 +2732,7 @@ EXPORT_SYMBOL(__page_symlink);
EXPORT_SYMBOL(page_symlink);
EXPORT_SYMBOL(page_symlink_inode_operations);
EXPORT_SYMBOL(path_lookup);
+EXPORT_SYMBOL(do_path_lookup);
EXPORT_SYMBOL(path_release);
EXPORT_SYMBOL(path_walk);
EXPORT_SYMBOL(permission);
diff -rup -X linux-2.6.18-rc2/Documentation/dontdiff
linux-2.6.18-rc2/include/linux/fs.h
linux-2.6.18-rc2-kb0/include/linux/fs.h
--- linux-2.6.18-rc2/include/linux/fs.h	2006-07-17 18:53:25.000000000 -0700
+++ linux-2.6.18-rc2-kb0/include/linux/fs.h	2006-07-17 17:30:58.000000000 -0700
@@ -91,6 +91,8 @@ extern int dir_notify_enable;
/* public flags for file_system_type */
#define FS_REQUIRES_DEV 1
#define FS_BINARY_MOUNTDATA 2
+#define FS_ALLOW_HLINK_DIR 4
+#define FS_OWN_LOCKS 8
#define FS_REVAL_DOT	16384	/* Check the paths ".", ".." for staleness */
#define FS_ODD_RENAME	32768	/* Temporary stuff; will go away as soon
				  * as nfs_rename() will be cleaned up
Only in linux-2.6.18-rc2-kb0/include/linux: utsrelease.h
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux