Skip to contentSkip to conversation

tmpfs: incr. link-count on directory rename

PATCH In review Uli Martens 4 participants 4 messages Feb 14, 2002 – Feb 20, 2002

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 tmpfs: incr. link-count on directory rename

Hi Christoph, hi all!

When I move a directory into another on a tmpfs filesystem, the link-count of the new parent directory isn't getting incremented. (which leads to find getting hickup, which eg. lets dpkg produce a "bzip2" binary package without binaries, generally not a nice thing)

test:

isax@home:/tmp/test$ mkdir t1 
isax@home:/tmp/test$ mkdir t2 
isax@home:/tmp/test$ mv t2 t1 
isax@home:/tmp/test$ ls -lRa 
.: 
total 0 
drwxr-xr-x    5 isax     isax            0 Feb 13 23:05 . 
drwxrwxrwt   13 root     root            0 Feb 13 23:04 .. 
drwxr-xr-x    2 isax     isax            0 Feb 13 23:05 t1 

./t1:
total 0

drwxr-xr-x    2 isax     isax            0 Feb 13 23:05 . 
drwxr-xr-x    5 isax     isax            0 Feb 13 23:05 .. 
drwxr-xr-x    2 isax     isax            0 Feb 13 23:05 t2 

./t1/t2:
total 0

drwxr-xr-x    2 isax     isax            0 Feb 13 23:05 . 
drwxr-xr-x    2 isax     isax            0 Feb 13 23:05 .. 

the link count of "t1", "t1/." and "t1/t2/.." should be "3", not "2". The following patch seems to work fine for me and is tested on my machine running debian's 2.4.17-1 and user-mode-linux 2.5.1-1. This is my first patch to the kernel, so I suppose there is a really huge mistake in my patch I don't see now...

mm/shmem.c Wed Feb 13 18:09:04 2002 renamed from mm/shmem.orig Wed Feb 13 00:56:14 2002 +3 -0
@@ -1085,6 +1085,9 @@
 {
        int error = -ENOTEMPTY;
 
+       if (S_ISDIR(old_dentry->d_inode->i_mode)) {
+               new_dir->i_nlink++;
+       }
        if (shmem_empty(new_dentry)) {
                struct inode *inode = new_dentry->d_inode;
                if (inode) {
Signature · 1 line
uli martens
#1 lore raw
Quoted 4 lines · Uli Martens

When I move a directory into another on a tmpfs filesystem, the link-count of the new parent directory isn't getting incremented. (which leads to find getting hickup, which eg. lets dpkg produce a "bzip2" binary package without binaries, generally not a nice thing)

Yeah, that optimization actually breaks on so many filesystems (AFS/Coda/iso9660/msdos/any filesystem which contains more than 65538 subdirectories in a directory/you name it) that they even provided a flag to disable it (-noleaf), which in my opinion should always be the default setting.

Another trick on the 'filesystem side' of things is to set the directory linkcount to 1. Basically find always subtracts 2 and then as long as the count is not zero, it will stat the entry to see if it's a directory. So a directory linkcount of 1 will 'underflow' and find will work correctly for the first 65535 sub-directories.

It's been on my todo list to use this fix in Coda as we have some link count issues when we're flipping entries between 'faked' symlinks for mountpoints/conflicts and real directories.

Jan

Review line 1093

Quoted 10 lines
(quoted diff) +3 -0
@@ -1085,6 +1085,9 @@
 {
        int error = -ENOTEMPTY;
 
+       if (S_ISDIR(old_dentry->d_inode->i_mode)) {
+               new_dir->i_nlink++;
+       }
        if (shmem_empty(new_dentry)) {
                struct inode *inode = new_dentry->d_inode;
                if (inode) {

ps. shouldn't the linkcount of the old_dir get decremented too? Also you should only change the linkcounts when the operation completed successfully. i.e. something more like,

tmp/shmem.c renamed from tmp/shmem.c.orig +4 -0
@@ -1100,6 +1100,10 @@
 		error = 0;
 		old_dentry->d_inode->i_ctime = old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
 	}
+	if (!error && S_ISDIR(old_dentry->d_inode->i_mode)) {
+	    old_dir->i_nlink--;
+	    new_dir->i_nlink++;
+	}
 	return error;
 }
 
#2 lore raw
Quoted 4 lines · Jan Harkes

ps. shouldn't the linkcount of the old_dir get decremented too? Also you should only change the linkcounts when the operation completed successfully. i.e. something more like,

Oops, you're right, the linkcount of old_dir isn't decremented at the moment, too. I will test your patch this evening, but I think it looks better than mine... ;)

Quoted 12 lines
tmp/shmem.c renamed from tmp/shmem.c.orig +4 -0
@@ -1100,6 +1100,10 @@
 		error = 0;
 		old_dentry->d_inode->i_ctime = old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
 	}
+	if (!error && S_ISDIR(old_dentry->d_inode->i_mode)) {
+	    old_dir->i_nlink--;
+	    new_dir->i_nlink++;
+	}
 	return error;
 }
Signature · 1 line
uli martens
#3 lore raw
In reply to Uli Martens

Hi Uli,

Quoted 3 lines · Uli Martens

Oops, you're right, the linkcount of old_dir isn't decremented at the moment, too. I will test your patch this evening, but I think it looks better than mine... ;)

I prefer this one.

Linus, Marcelo, please apply.

The patch fixes the refcounting of directories on renames in tmpfs.

Greetings
Christoph

mm/shmem.c +17 -11
diff -uNr 18-rc1/mm/shmem.c c/mm/shmem.c
@@ -1083,19 +1083,25 @@
  */
 static int shmem_rename(struct inode * old_dir, struct dentry *old_dentry, struct inode * new_dir,struct dentry *new_dentry)
 {
-	int error = -ENOTEMPTY;
+	struct inode *inode;
 
-	if (shmem_empty(new_dentry)) {
-		struct inode *inode = new_dentry->d_inode;
-		if (inode) {
-			inode->i_ctime = CURRENT_TIME;
-			inode->i_nlink--;
-			dput(new_dentry);
-		}
-		error = 0;
-		old_dentry->d_inode->i_ctime = old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+	if (!shmem_empty(new_dentry)) 
+		return -ENOTEMPTY;
+
+	inode = new_dentry->d_inode;
+	if (inode) {
+		inode->i_ctime = CURRENT_TIME;
+		inode->i_nlink--;
+		dput(new_dentry);
+	}
+	inode = old_dentry->d_inode;
+	if (S_ISDIR(inode->i_mode)) {
+		old_dir->i_nlink--;
+		new_dir->i_nlink++;
 	}
-	return error;
+
+	inode->i_ctime = old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+	return 0;
 }
 
 static int shmem_symlink(struct inode * dir, struct dentry *dentry, const char * symname)