@@ -202,7 +202,7 @@ zpl_snapdir_revalidate(struct dentry *dentry, unsigned int flags)
202
202
return (!!dentry -> d_inode );
203
203
}
204
204
205
- static dentry_operations_t zpl_dops_snapdirs = {
205
+ static const struct dentry_operations zpl_dops_snapdirs = {
206
206
/*
207
207
* Auto mounting of snapshots is only supported for 2.6.37 and
208
208
* newer kernels. Prior to this kernel the ops->follow_link()
@@ -215,6 +215,51 @@ static dentry_operations_t zpl_dops_snapdirs = {
215
215
.d_revalidate = zpl_snapdir_revalidate ,
216
216
};
217
217
218
+ /*
219
+ * For the .zfs control directory to work properly we must be able to override
220
+ * the default operations table and register custom .d_automount and
221
+ * .d_revalidate callbacks.
222
+ */
223
+ static void
224
+ set_snapdir_dentry_ops (struct dentry * dentry , unsigned int extraflags ) {
225
+ static const unsigned int op_flags =
226
+ DCACHE_OP_HASH | DCACHE_OP_COMPARE |
227
+ DCACHE_OP_REVALIDATE | DCACHE_OP_DELETE |
228
+ DCACHE_OP_PRUNE | DCACHE_OP_WEAK_REVALIDATE | DCACHE_OP_REAL ;
229
+
230
+ #ifdef HAVE_D_SET_D_OP
231
+ /*
232
+ * d_set_d_op() will set the DCACHE_OP_ flags according to what it
233
+ * finds in the passed dentry_operations, so we don't have to.
234
+ *
235
+ * We clear the flags and the old op table before calling d_set_d_op()
236
+ * because issues a warning when the dentry operations table is already
237
+ * set.
238
+ */
239
+ dentry -> d_op = NULL ;
240
+ dentry -> d_flags &= ~op_flags ;
241
+ d_set_d_op (dentry , & zpl_dops_snapdirs );
242
+ dentry -> d_flags |= extraflags ;
243
+ #else
244
+ /*
245
+ * Since 6.17 there's no exported way to modify dentry ops, so we have
246
+ * to reach in and do it ourselves. This should be safe for our very
247
+ * narrow use case, which is to create or splice in an entry to give
248
+ * access to a snapshot.
249
+ *
250
+ * We need to set the op flags directly. We hardcode
251
+ * DCACHE_OP_REVALIDATE because that's the only operation we have; if
252
+ * we ever extend zpl_dops_snapdirs we will need to update the op flags
253
+ * to match.
254
+ */
255
+ spin_lock (& dentry -> d_lock );
256
+ dentry -> d_op = & zpl_dops_snapdirs ;
257
+ dentry -> d_flags &= ~op_flags ;
258
+ dentry -> d_flags |= DCACHE_OP_REVALIDATE | extraflags ;
259
+ spin_unlock (& dentry -> d_lock );
260
+ #endif
261
+ }
262
+
218
263
static struct dentry *
219
264
zpl_snapdir_lookup (struct inode * dip , struct dentry * dentry ,
220
265
unsigned int flags )
@@ -236,10 +281,7 @@ zpl_snapdir_lookup(struct inode *dip, struct dentry *dentry,
236
281
return (ERR_PTR (error ));
237
282
238
283
ASSERT (error == 0 || ip == NULL );
239
- d_clear_d_op (dentry );
240
- d_set_d_op (dentry , & zpl_dops_snapdirs );
241
- dentry -> d_flags |= DCACHE_NEED_AUTOMOUNT ;
242
-
284
+ set_snapdir_dentry_ops (dentry , DCACHE_NEED_AUTOMOUNT );
243
285
return (d_splice_alias (ip , dentry ));
244
286
}
245
287
@@ -373,8 +415,7 @@ zpl_snapdir_mkdir(struct inode *dip, struct dentry *dentry, umode_t mode)
373
415
374
416
error = - zfsctl_snapdir_mkdir (dip , dname (dentry ), vap , & ip , cr , 0 );
375
417
if (error == 0 ) {
376
- d_clear_d_op (dentry );
377
- d_set_d_op (dentry , & zpl_dops_snapdirs );
418
+ set_snapdir_dentry_ops (dentry , 0 );
378
419
d_instantiate (dentry , ip );
379
420
}
380
421
0 commit comments