diff options
author | Mark Fasheh <mark.fasheh@oracle.com> | 2006-10-01 02:29:05 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-10-01 03:39:30 -0400 |
commit | 17ff785691503f63ec648df82a7fdaece7695561 (patch) | |
tree | 9777f4787253941c723aac1e13ab37fb285bcc4a /fs/ocfs2 | |
parent | d8c76e6f45c111c32a4b3e50a2adc9210737b0d8 (diff) |
[PATCH] r/o bind mounts: clean up OCFS2 nlink handling
OCFS2 does some operations on i_nlink, then reverts them if some of its
operations fail to complete. This does not fit in well with the
drop_nlink() logic where we expect i_nlink to stay at zero once it gets
there.
So, delay all of the nlink operations until we're sure that the operations
have completed. Also, introduce a small helper to check whether an inode
has proper "unlinkable" i_nlink counts no matter whether it is a directory
or regular inode.
This patch is broken out from the others because it does contain some
logical changes.
Signed-off-by: Dave Hansen <haveblue@us.ibm.com>
Signed-off-by: Mark Fasheh <mark.fasheh@oracle.com>
Cc: 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>
Diffstat (limited to 'fs/ocfs2')
-rw-r--r-- | fs/ocfs2/namei.c | 39 |
1 files changed, 19 insertions, 20 deletions
diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index 8c370a39e0c1..259155f0eb2e 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c | |||
@@ -795,11 +795,23 @@ static int ocfs2_remote_dentry_delete(struct dentry *dentry) | |||
795 | return ret; | 795 | return ret; |
796 | } | 796 | } |
797 | 797 | ||
798 | static inline int inode_is_unlinkable(struct inode *inode) | ||
799 | { | ||
800 | if (S_ISDIR(inode->i_mode)) { | ||
801 | if (inode->i_nlink == 2) | ||
802 | return 1; | ||
803 | return 0; | ||
804 | } | ||
805 | |||
806 | if (inode->i_nlink == 1) | ||
807 | return 1; | ||
808 | return 0; | ||
809 | } | ||
810 | |||
798 | static int ocfs2_unlink(struct inode *dir, | 811 | static int ocfs2_unlink(struct inode *dir, |
799 | struct dentry *dentry) | 812 | struct dentry *dentry) |
800 | { | 813 | { |
801 | int status; | 814 | int status; |
802 | unsigned int saved_nlink = 0; | ||
803 | struct inode *inode = dentry->d_inode; | 815 | struct inode *inode = dentry->d_inode; |
804 | struct ocfs2_super *osb = OCFS2_SB(dir->i_sb); | 816 | struct ocfs2_super *osb = OCFS2_SB(dir->i_sb); |
805 | u64 blkno; | 817 | u64 blkno; |
@@ -874,16 +886,6 @@ static int ocfs2_unlink(struct inode *dir, | |||
874 | } | 886 | } |
875 | } | 887 | } |
876 | 888 | ||
877 | /* There are still a few steps left until we can consider the | ||
878 | * unlink to have succeeded. Save off nlink here before | ||
879 | * modification so we can set it back in case we hit an issue | ||
880 | * before commit. */ | ||
881 | saved_nlink = inode->i_nlink; | ||
882 | if (S_ISDIR(inode->i_mode)) | ||
883 | inode->i_nlink = 0; | ||
884 | else | ||
885 | inode->i_nlink--; | ||
886 | |||
887 | status = ocfs2_remote_dentry_delete(dentry); | 889 | status = ocfs2_remote_dentry_delete(dentry); |
888 | if (status < 0) { | 890 | if (status < 0) { |
889 | /* This vote should succeed under all normal | 891 | /* This vote should succeed under all normal |
@@ -892,7 +894,7 @@ static int ocfs2_unlink(struct inode *dir, | |||
892 | goto leave; | 894 | goto leave; |
893 | } | 895 | } |
894 | 896 | ||
895 | if (!inode->i_nlink) { | 897 | if (inode_is_unlinkable(inode)) { |
896 | status = ocfs2_prepare_orphan_dir(osb, handle, inode, | 898 | status = ocfs2_prepare_orphan_dir(osb, handle, inode, |
897 | orphan_name, | 899 | orphan_name, |
898 | &orphan_entry_bh); | 900 | &orphan_entry_bh); |
@@ -919,7 +921,7 @@ static int ocfs2_unlink(struct inode *dir, | |||
919 | 921 | ||
920 | fe = (struct ocfs2_dinode *) fe_bh->b_data; | 922 | fe = (struct ocfs2_dinode *) fe_bh->b_data; |
921 | 923 | ||
922 | if (!inode->i_nlink) { | 924 | if (inode_is_unlinkable(inode)) { |
923 | status = ocfs2_orphan_add(osb, handle, inode, fe, orphan_name, | 925 | status = ocfs2_orphan_add(osb, handle, inode, fe, orphan_name, |
924 | orphan_entry_bh); | 926 | orphan_entry_bh); |
925 | if (status < 0) { | 927 | if (status < 0) { |
@@ -935,10 +937,10 @@ static int ocfs2_unlink(struct inode *dir, | |||
935 | goto leave; | 937 | goto leave; |
936 | } | 938 | } |
937 | 939 | ||
938 | /* We can set nlink on the dinode now. clear the saved version | 940 | if (S_ISDIR(inode->i_mode)) |
939 | * so that it doesn't get set later. */ | 941 | drop_nlink(inode); |
942 | drop_nlink(inode); | ||
940 | fe->i_links_count = cpu_to_le16(inode->i_nlink); | 943 | fe->i_links_count = cpu_to_le16(inode->i_nlink); |
941 | saved_nlink = 0; | ||
942 | 944 | ||
943 | status = ocfs2_journal_dirty(handle, fe_bh); | 945 | status = ocfs2_journal_dirty(handle, fe_bh); |
944 | if (status < 0) { | 946 | if (status < 0) { |
@@ -947,7 +949,7 @@ static int ocfs2_unlink(struct inode *dir, | |||
947 | } | 949 | } |
948 | 950 | ||
949 | if (S_ISDIR(inode->i_mode)) { | 951 | if (S_ISDIR(inode->i_mode)) { |
950 | dir->i_nlink--; | 952 | drop_nlink(dir); |
951 | status = ocfs2_mark_inode_dirty(handle, dir, | 953 | status = ocfs2_mark_inode_dirty(handle, dir, |
952 | parent_node_bh); | 954 | parent_node_bh); |
953 | if (status < 0) { | 955 | if (status < 0) { |
@@ -957,9 +959,6 @@ static int ocfs2_unlink(struct inode *dir, | |||
957 | } | 959 | } |
958 | 960 | ||
959 | leave: | 961 | leave: |
960 | if (status < 0 && saved_nlink) | ||
961 | inode->i_nlink = saved_nlink; | ||
962 | |||
963 | if (handle) | 962 | if (handle) |
964 | ocfs2_commit_trans(handle); | 963 | ocfs2_commit_trans(handle); |
965 | 964 | ||