diff options
author | Christoph Hellwig <hch@infradead.org> | 2008-04-22 03:34:06 -0400 |
---|---|---|
committer | Lachlan McIlroy <lachlan@redback.melbourne.sgi.com> | 2008-04-29 01:54:12 -0400 |
commit | cfa853e47df4fbee441ac0ac3fb592f076233145 (patch) | |
tree | 7e4baafba5ee0f05561580c301c5d360236063f3 /fs/xfs/xfs_rename.c | |
parent | 579aa9caf552c639fc78168db4cfe7ffcf00c3b3 (diff) |
[XFS] remove manual lookup from xfs_rename and simplify locking
->rename already gets the target inode passed if it exits. Pass it down to
xfs_rename so that we can avoid looking it up again. Also simplify locking
as the first lock section in xfs_rename can go away now: the isdir is an
invariant over the lifetime of the inode, and new_parent and the nlink
check are namespace topology protected by i_mutex in the VFS. The projid
check needs to move into the second lock section anyway to not be racy.
Also kill the now unused xfs_dir_lookup_int and remove the now-unused
first_locked argumet to xfs_lock_inodes.
SGI-PV: 976035
SGI-Modid: xfs-linux-melb:xfs-kern:30903a
Signed-off-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Lachlan McIlroy <lachlan@sgi.com>
Diffstat (limited to 'fs/xfs/xfs_rename.c')
-rw-r--r-- | fs/xfs/xfs_rename.c | 185 |
1 files changed, 45 insertions, 140 deletions
diff --git a/fs/xfs/xfs_rename.c b/fs/xfs/xfs_rename.c index ee371890d85d..6a141427f68a 100644 --- a/fs/xfs/xfs_rename.c +++ b/fs/xfs/xfs_rename.c | |||
@@ -55,85 +55,32 @@ xfs_rename_unlock4( | |||
55 | 55 | ||
56 | xfs_iunlock(i_tab[0], lock_mode); | 56 | xfs_iunlock(i_tab[0], lock_mode); |
57 | for (i = 1; i < 4; i++) { | 57 | for (i = 1; i < 4; i++) { |
58 | if (i_tab[i] == NULL) { | 58 | if (i_tab[i] == NULL) |
59 | break; | 59 | break; |
60 | } | 60 | |
61 | /* | 61 | /* |
62 | * Watch out for duplicate entries in the table. | 62 | * Watch out for duplicate entries in the table. |
63 | */ | 63 | */ |
64 | if (i_tab[i] != i_tab[i-1]) { | 64 | if (i_tab[i] != i_tab[i-1]) |
65 | xfs_iunlock(i_tab[i], lock_mode); | 65 | xfs_iunlock(i_tab[i], lock_mode); |
66 | } | ||
67 | } | 66 | } |
68 | } | 67 | } |
69 | 68 | ||
70 | #ifdef DEBUG | ||
71 | int xfs_rename_skip, xfs_rename_nskip; | ||
72 | #endif | ||
73 | |||
74 | /* | 69 | /* |
75 | * The following routine will acquire the locks required for a rename | 70 | * Enter all inodes for a rename transaction into a sorted array. |
76 | * operation. The code understands the semantics of renames and will | ||
77 | * validate that name1 exists under dp1 & that name2 may or may not | ||
78 | * exist under dp2. | ||
79 | * | ||
80 | * We are renaming dp1/name1 to dp2/name2. | ||
81 | * | ||
82 | * Return ENOENT if dp1 does not exist, other lookup errors, or 0 for success. | ||
83 | */ | 71 | */ |
84 | STATIC int | 72 | STATIC void |
85 | xfs_lock_for_rename( | 73 | xfs_sort_for_rename( |
86 | xfs_inode_t *dp1, /* in: old (source) directory inode */ | 74 | xfs_inode_t *dp1, /* in: old (source) directory inode */ |
87 | xfs_inode_t *dp2, /* in: new (target) directory inode */ | 75 | xfs_inode_t *dp2, /* in: new (target) directory inode */ |
88 | xfs_inode_t *ip1, /* in: inode of old entry */ | 76 | xfs_inode_t *ip1, /* in: inode of old entry */ |
89 | struct xfs_name *name2, /* in: new entry name */ | 77 | xfs_inode_t *ip2, /* in: inode of new entry, if it |
90 | xfs_inode_t **ipp2, /* out: inode of new entry, if it | ||
91 | already exists, NULL otherwise. */ | 78 | already exists, NULL otherwise. */ |
92 | xfs_inode_t **i_tab,/* out: array of inode returned, sorted */ | 79 | xfs_inode_t **i_tab,/* out: array of inode returned, sorted */ |
93 | int *num_inodes) /* out: number of inodes in array */ | 80 | int *num_inodes) /* out: number of inodes in array */ |
94 | { | 81 | { |
95 | xfs_inode_t *ip2 = NULL; | ||
96 | xfs_inode_t *temp; | 82 | xfs_inode_t *temp; |
97 | xfs_ino_t inum1, inum2; | ||
98 | int error; | ||
99 | int i, j; | 83 | int i, j; |
100 | uint lock_mode; | ||
101 | int diff_dirs = (dp1 != dp2); | ||
102 | |||
103 | /* | ||
104 | * First, find out the current inums of the entries so that we | ||
105 | * can determine the initial locking order. We'll have to | ||
106 | * sanity check stuff after all the locks have been acquired | ||
107 | * to see if we still have the right inodes, directories, etc. | ||
108 | */ | ||
109 | lock_mode = xfs_ilock_map_shared(dp1); | ||
110 | IHOLD(ip1); | ||
111 | xfs_itrace_ref(ip1); | ||
112 | |||
113 | inum1 = ip1->i_ino; | ||
114 | |||
115 | /* | ||
116 | * Unlock dp1 and lock dp2 if they are different. | ||
117 | */ | ||
118 | if (diff_dirs) { | ||
119 | xfs_iunlock_map_shared(dp1, lock_mode); | ||
120 | lock_mode = xfs_ilock_map_shared(dp2); | ||
121 | } | ||
122 | |||
123 | error = xfs_dir_lookup_int(dp2, lock_mode, name2, &inum2, &ip2); | ||
124 | if (error == ENOENT) { /* target does not need to exist. */ | ||
125 | inum2 = 0; | ||
126 | } else if (error) { | ||
127 | /* | ||
128 | * If dp2 and dp1 are the same, the next line unlocks dp1. | ||
129 | * Got it? | ||
130 | */ | ||
131 | xfs_iunlock_map_shared(dp2, lock_mode); | ||
132 | IRELE (ip1); | ||
133 | return error; | ||
134 | } else { | ||
135 | xfs_itrace_ref(ip2); | ||
136 | } | ||
137 | 84 | ||
138 | /* | 85 | /* |
139 | * i_tab contains a list of pointers to inodes. We initialize | 86 | * i_tab contains a list of pointers to inodes. We initialize |
@@ -145,21 +92,20 @@ xfs_lock_for_rename( | |||
145 | i_tab[0] = dp1; | 92 | i_tab[0] = dp1; |
146 | i_tab[1] = dp2; | 93 | i_tab[1] = dp2; |
147 | i_tab[2] = ip1; | 94 | i_tab[2] = ip1; |
148 | if (inum2 == 0) { | 95 | if (ip2) { |
149 | *num_inodes = 3; | ||
150 | i_tab[3] = NULL; | ||
151 | } else { | ||
152 | *num_inodes = 4; | 96 | *num_inodes = 4; |
153 | i_tab[3] = ip2; | 97 | i_tab[3] = ip2; |
98 | } else { | ||
99 | *num_inodes = 3; | ||
100 | i_tab[3] = NULL; | ||
154 | } | 101 | } |
155 | *ipp2 = i_tab[3]; | ||
156 | 102 | ||
157 | /* | 103 | /* |
158 | * Sort the elements via bubble sort. (Remember, there are at | 104 | * Sort the elements via bubble sort. (Remember, there are at |
159 | * most 4 elements to sort, so this is adequate.) | 105 | * most 4 elements to sort, so this is adequate.) |
160 | */ | 106 | */ |
161 | for (i=0; i < *num_inodes; i++) { | 107 | for (i = 0; i < *num_inodes; i++) { |
162 | for (j=1; j < *num_inodes; j++) { | 108 | for (j = 1; j < *num_inodes; j++) { |
163 | if (i_tab[j]->i_ino < i_tab[j-1]->i_ino) { | 109 | if (i_tab[j]->i_ino < i_tab[j-1]->i_ino) { |
164 | temp = i_tab[j]; | 110 | temp = i_tab[j]; |
165 | i_tab[j] = i_tab[j-1]; | 111 | i_tab[j] = i_tab[j-1]; |
@@ -167,30 +113,6 @@ xfs_lock_for_rename( | |||
167 | } | 113 | } |
168 | } | 114 | } |
169 | } | 115 | } |
170 | |||
171 | /* | ||
172 | * We have dp2 locked. If it isn't first, unlock it. | ||
173 | * If it is first, tell xfs_lock_inodes so it can skip it | ||
174 | * when locking. if dp1 == dp2, xfs_lock_inodes will skip both | ||
175 | * since they are equal. xfs_lock_inodes needs all these inodes | ||
176 | * so that it can unlock and retry if there might be a dead-lock | ||
177 | * potential with the log. | ||
178 | */ | ||
179 | |||
180 | if (i_tab[0] == dp2 && lock_mode == XFS_ILOCK_SHARED) { | ||
181 | #ifdef DEBUG | ||
182 | xfs_rename_skip++; | ||
183 | #endif | ||
184 | xfs_lock_inodes(i_tab, *num_inodes, 1, XFS_ILOCK_SHARED); | ||
185 | } else { | ||
186 | #ifdef DEBUG | ||
187 | xfs_rename_nskip++; | ||
188 | #endif | ||
189 | xfs_iunlock_map_shared(dp2, lock_mode); | ||
190 | xfs_lock_inodes(i_tab, *num_inodes, 0, XFS_ILOCK_SHARED); | ||
191 | } | ||
192 | |||
193 | return 0; | ||
194 | } | 116 | } |
195 | 117 | ||
196 | /* | 118 | /* |
@@ -202,10 +124,10 @@ xfs_rename( | |||
202 | struct xfs_name *src_name, | 124 | struct xfs_name *src_name, |
203 | xfs_inode_t *src_ip, | 125 | xfs_inode_t *src_ip, |
204 | xfs_inode_t *target_dp, | 126 | xfs_inode_t *target_dp, |
205 | struct xfs_name *target_name) | 127 | struct xfs_name *target_name, |
128 | xfs_inode_t *target_ip) | ||
206 | { | 129 | { |
207 | xfs_trans_t *tp; | 130 | xfs_trans_t *tp = NULL; |
208 | xfs_inode_t *target_ip; | ||
209 | xfs_mount_t *mp = src_dp->i_mount; | 131 | xfs_mount_t *mp = src_dp->i_mount; |
210 | int new_parent; /* moving to a new dir */ | 132 | int new_parent; /* moving to a new dir */ |
211 | int src_is_directory; /* src_name is a directory */ | 133 | int src_is_directory; /* src_name is a directory */ |
@@ -230,64 +152,31 @@ xfs_rename( | |||
230 | target_dp, DM_RIGHT_NULL, | 152 | target_dp, DM_RIGHT_NULL, |
231 | src_name->name, target_name->name, | 153 | src_name->name, target_name->name, |
232 | 0, 0, 0); | 154 | 0, 0, 0); |
233 | if (error) { | 155 | if (error) |
234 | return error; | 156 | return error; |
235 | } | ||
236 | } | 157 | } |
237 | /* Return through std_return after this point. */ | 158 | /* Return through std_return after this point. */ |
238 | 159 | ||
239 | /* | 160 | new_parent = (src_dp != target_dp); |
240 | * Lock all the participating inodes. Depending upon whether | 161 | src_is_directory = ((src_ip->i_d.di_mode & S_IFMT) == S_IFDIR); |
241 | * the target_name exists in the target directory, and | ||
242 | * whether the target directory is the same as the source | ||
243 | * directory, we can lock from 2 to 4 inodes. | ||
244 | * xfs_lock_for_rename() will return ENOENT if src_name | ||
245 | * does not exist in the source directory. | ||
246 | */ | ||
247 | tp = NULL; | ||
248 | error = xfs_lock_for_rename(src_dp, target_dp, src_ip, target_name, | ||
249 | &target_ip, inodes, &num_inodes); | ||
250 | if (error) { | ||
251 | /* | ||
252 | * We have nothing locked, no inode references, and | ||
253 | * no transaction, so just get out. | ||
254 | */ | ||
255 | goto std_return; | ||
256 | } | ||
257 | |||
258 | ASSERT(src_ip != NULL); | ||
259 | 162 | ||
260 | if ((src_ip->i_d.di_mode & S_IFMT) == S_IFDIR) { | 163 | if (src_is_directory) { |
261 | /* | 164 | /* |
262 | * Check for link count overflow on target_dp | 165 | * Check for link count overflow on target_dp |
263 | */ | 166 | */ |
264 | if (target_ip == NULL && (src_dp != target_dp) && | 167 | if (target_ip == NULL && new_parent && |
265 | target_dp->i_d.di_nlink >= XFS_MAXLINK) { | 168 | target_dp->i_d.di_nlink >= XFS_MAXLINK) { |
266 | error = XFS_ERROR(EMLINK); | 169 | error = XFS_ERROR(EMLINK); |
267 | xfs_rename_unlock4(inodes, XFS_ILOCK_SHARED); | 170 | goto std_return; |
268 | goto rele_return; | ||
269 | } | 171 | } |
270 | } | 172 | } |
271 | 173 | ||
272 | /* | 174 | xfs_sort_for_rename(src_dp, target_dp, src_ip, target_ip, |
273 | * If we are using project inheritance, we only allow renames | 175 | inodes, &num_inodes); |
274 | * into our tree when the project IDs are the same; else the | ||
275 | * tree quota mechanism would be circumvented. | ||
276 | */ | ||
277 | if (unlikely((target_dp->i_d.di_flags & XFS_DIFLAG_PROJINHERIT) && | ||
278 | (target_dp->i_d.di_projid != src_ip->i_d.di_projid))) { | ||
279 | error = XFS_ERROR(EXDEV); | ||
280 | xfs_rename_unlock4(inodes, XFS_ILOCK_SHARED); | ||
281 | goto rele_return; | ||
282 | } | ||
283 | |||
284 | new_parent = (src_dp != target_dp); | ||
285 | src_is_directory = ((src_ip->i_d.di_mode & S_IFMT) == S_IFDIR); | ||
286 | 176 | ||
287 | /* | 177 | IHOLD(src_ip); |
288 | * Drop the locks on our inodes so that we can start the transaction. | 178 | if (target_ip) |
289 | */ | 179 | IHOLD(target_ip); |
290 | xfs_rename_unlock4(inodes, XFS_ILOCK_SHARED); | ||
291 | 180 | ||
292 | XFS_BMAP_INIT(&free_list, &first_block); | 181 | XFS_BMAP_INIT(&free_list, &first_block); |
293 | tp = xfs_trans_alloc(mp, XFS_TRANS_RENAME); | 182 | tp = xfs_trans_alloc(mp, XFS_TRANS_RENAME); |
@@ -314,9 +203,25 @@ xfs_rename( | |||
314 | } | 203 | } |
315 | 204 | ||
316 | /* | 205 | /* |
317 | * Reacquire the inode locks we dropped above. | 206 | * Lock all the participating inodes. Depending upon whether |
207 | * the target_name exists in the target directory, and | ||
208 | * whether the target directory is the same as the source | ||
209 | * directory, we can lock from 2 to 4 inodes. | ||
318 | */ | 210 | */ |
319 | xfs_lock_inodes(inodes, num_inodes, 0, XFS_ILOCK_EXCL); | 211 | xfs_lock_inodes(inodes, num_inodes, XFS_ILOCK_EXCL); |
212 | |||
213 | /* | ||
214 | * If we are using project inheritance, we only allow renames | ||
215 | * into our tree when the project IDs are the same; else the | ||
216 | * tree quota mechanism would be circumvented. | ||
217 | */ | ||
218 | if (unlikely((target_dp->i_d.di_flags & XFS_DIFLAG_PROJINHERIT) && | ||
219 | (target_dp->i_d.di_projid != src_ip->i_d.di_projid))) { | ||
220 | error = XFS_ERROR(EXDEV); | ||
221 | xfs_rename_unlock4(inodes, XFS_ILOCK_SHARED); | ||
222 | xfs_trans_cancel(tp, cancel_flags); | ||
223 | goto rele_return; | ||
224 | } | ||
320 | 225 | ||
321 | /* | 226 | /* |
322 | * Join all the inodes to the transaction. From this point on, | 227 | * Join all the inodes to the transaction. From this point on, |