@@ -125,6 +125,86 @@ static void clear_disconnected(struct dentry *dentry)
125
125
dput (dentry );
126
126
}
127
127
128
+ /*
129
+ * Reconnect a directory dentry with its parent.
130
+ *
131
+ * This can return a dentry, or NULL, or an error.
132
+ *
133
+ * In the first case the returned dentry is the parent of the given
134
+ * dentry, and may itself need to be reconnected to its parent.
135
+ *
136
+ * In the NULL case, a concurrent VFS operation has either renamed or
137
+ * removed this directory. The concurrent operation has reconnected our
138
+ * dentry, so we no longer need to.
139
+ */
140
+ static struct dentry * reconnect_one (struct vfsmount * mnt ,
141
+ struct dentry * dentry , char * nbuf )
142
+ {
143
+ struct dentry * parent ;
144
+ struct dentry * tmp ;
145
+ int err ;
146
+
147
+ parent = ERR_PTR (- EACCES );
148
+ mutex_lock (& dentry -> d_inode -> i_mutex );
149
+ if (mnt -> mnt_sb -> s_export_op -> get_parent )
150
+ parent = mnt -> mnt_sb -> s_export_op -> get_parent (dentry );
151
+ mutex_unlock (& dentry -> d_inode -> i_mutex );
152
+
153
+ if (IS_ERR (parent )) {
154
+ dprintk ("%s: get_parent of %ld failed, err %d\n" ,
155
+ __func__ , dentry -> d_inode -> i_ino , PTR_ERR (parent ));
156
+ return parent ;
157
+ }
158
+
159
+ dprintk ("%s: find name of %lu in %lu\n" , __func__ ,
160
+ dentry -> d_inode -> i_ino , parent -> d_inode -> i_ino );
161
+ err = exportfs_get_name (mnt , parent , nbuf , dentry );
162
+ if (err == - ENOENT )
163
+ goto out_reconnected ;
164
+ if (err )
165
+ goto out_err ;
166
+ dprintk ("%s: found name: %s\n" , __func__ , nbuf );
167
+ mutex_lock (& parent -> d_inode -> i_mutex );
168
+ tmp = lookup_one_len (nbuf , parent , strlen (nbuf ));
169
+ mutex_unlock (& parent -> d_inode -> i_mutex );
170
+ if (IS_ERR (tmp )) {
171
+ dprintk ("%s: lookup failed: %d\n" , __func__ , PTR_ERR (tmp ));
172
+ goto out_err ;
173
+ }
174
+ if (tmp != dentry ) {
175
+ dput (tmp );
176
+ goto out_reconnected ;
177
+ }
178
+ dput (tmp );
179
+ if (IS_ROOT (dentry )) {
180
+ err = - ESTALE ;
181
+ goto out_err ;
182
+ }
183
+ return parent ;
184
+
185
+ out_err :
186
+ dput (parent );
187
+ return ERR_PTR (err );
188
+ out_reconnected :
189
+ dput (parent );
190
+ /*
191
+ * Someone must have renamed our entry into another parent, in
192
+ * which case it has been reconnected by the rename.
193
+ *
194
+ * Or someone removed it entirely, in which case filehandle
195
+ * lookup will succeed but the directory is now IS_DEAD and
196
+ * subsequent operations on it will fail.
197
+ *
198
+ * Alternatively, maybe there was no race at all, and the
199
+ * filesystem is just corrupt and gave us a parent that doesn't
200
+ * actually contain any entry pointing to this inode. So,
201
+ * double check that this worked and return -ESTALE if not:
202
+ */
203
+ if (!dentry_connected (dentry ))
204
+ return ERR_PTR (- ESTALE );
205
+ return NULL ;
206
+ }
207
+
128
208
/*
129
209
* Make sure target_dir is fully connected to the dentry tree.
130
210
*
@@ -158,76 +238,19 @@ reconnect_path(struct vfsmount *mnt, struct dentry *target_dir, char *nbuf)
158
238
dput (pd );
159
239
break ;
160
240
} else {
241
+ struct dentry * parent ;
161
242
/*
162
243
* We have hit the top of a disconnected path, try to
163
244
* find parent and connect.
164
- *
165
- * Racing with some other process renaming a directory
166
- * isn't much of a problem here. If someone renames
167
- * the directory, it will end up properly connected,
168
- * which is what we want
169
- *
170
- * Getting the parent can't be supported generically,
171
- * the locking is too icky.
172
- *
173
- * Instead we just return EACCES. If server reboots
174
- * or inodes get flushed, you lose
175
- */
176
- struct dentry * ppd = ERR_PTR (- EACCES );
177
- struct dentry * npd ;
178
-
179
- mutex_lock (& pd -> d_inode -> i_mutex );
180
- if (mnt -> mnt_sb -> s_export_op -> get_parent )
181
- ppd = mnt -> mnt_sb -> s_export_op -> get_parent (pd );
182
- mutex_unlock (& pd -> d_inode -> i_mutex );
183
-
184
- if (IS_ERR (ppd )) {
185
- err = PTR_ERR (ppd );
186
- dprintk ("%s: get_parent of %ld failed, err %d\n" ,
187
- __func__ , pd -> d_inode -> i_ino , err );
188
- dput (pd );
189
- break ;
190
- }
191
-
192
- dprintk ("%s: find name of %lu in %lu\n" , __func__ ,
193
- pd -> d_inode -> i_ino , ppd -> d_inode -> i_ino );
194
- err = exportfs_get_name (mnt , ppd , nbuf , pd );
195
- if (err ) {
196
- dput (ppd );
197
- dput (pd );
198
- if (err == - ENOENT )
199
- /* some race between get_parent and
200
- * get_name?
201
- */
202
- goto out_reconnected ;
203
- break ;
204
- }
205
- dprintk ("%s: found name: %s\n" , __func__ , nbuf );
206
- mutex_lock (& ppd -> d_inode -> i_mutex );
207
- npd = lookup_one_len (nbuf , ppd , strlen (nbuf ));
208
- mutex_unlock (& ppd -> d_inode -> i_mutex );
209
- if (IS_ERR (npd )) {
210
- err = PTR_ERR (npd );
211
- dprintk ("%s: lookup failed: %d\n" ,
212
- __func__ , err );
213
- dput (ppd );
214
- dput (pd );
215
- break ;
216
- }
217
- /* we didn't really want npd, we really wanted
218
- * a side-effect of the lookup.
219
- * hopefully, npd == pd, though it isn't really
220
- * a problem if it isn't
221
245
*/
222
- dput (npd );
223
- dput (ppd );
224
- if (npd != pd )
246
+ parent = reconnect_one (mnt , pd , nbuf );
247
+ if (!parent )
225
248
goto out_reconnected ;
226
- if (IS_ROOT (pd )) {
227
- /* something went wrong, we have to give up */
228
- dput (pd );
249
+ if (IS_ERR (parent )) {
250
+ err = PTR_ERR (parent );
229
251
break ;
230
252
}
253
+ dput (parent );
231
254
}
232
255
dput (pd );
233
256
}
@@ -241,21 +264,6 @@ reconnect_path(struct vfsmount *mnt, struct dentry *target_dir, char *nbuf)
241
264
242
265
return 0 ;
243
266
out_reconnected :
244
- /*
245
- * Someone must have renamed our entry into another parent, in
246
- * which case it has been reconnected by the rename.
247
- *
248
- * Or someone removed it entirely, in which case filehandle
249
- * lookup will succeed but the directory is now IS_DEAD and
250
- * subsequent operations on it will fail.
251
- *
252
- * Alternatively, maybe there was no race at all, and the
253
- * filesystem is just corrupt and gave us a parent that doesn't
254
- * actually contain any entry pointing to this inode. So,
255
- * double check that this worked and return -ESTALE if not:
256
- */
257
- if (!dentry_connected (target_dir ))
258
- return - ESTALE ;
259
267
clear_disconnected (target_dir );
260
268
return 0 ;
261
269
}
0 commit comments