]>
Commit | Line | Data |
---|---|---|
ebe7e575 BB |
1 | /* |
2 | * CDDL HEADER START | |
3 | * | |
4 | * The contents of this file are subject to the terms of the | |
5 | * Common Development and Distribution License (the "License"). | |
6 | * You may not use this file except in compliance with the License. | |
7 | * | |
8 | * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE | |
1d3ba0bf | 9 | * or https://opensource.org/licenses/CDDL-1.0. |
ebe7e575 BB |
10 | * See the License for the specific language governing permissions |
11 | * and limitations under the License. | |
12 | * | |
13 | * When distributing Covered Code, include this CDDL HEADER in each | |
14 | * file and include the License file at usr/src/OPENSOLARIS.LICENSE. | |
15 | * If applicable, add the following below this CDDL HEADER, with the | |
16 | * fields enclosed by brackets "[]" replaced with your own identifying | |
17 | * information: Portions Copyright [yyyy] [name of copyright owner] | |
18 | * | |
19 | * CDDL HEADER END | |
20 | */ | |
21 | /* | |
22 | * Copyright (C) 2011 Lawrence Livermore National Security, LLC. | |
23 | * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). | |
24 | * LLNL-CODE-403049. | |
25 | * Rewritten for Linux by: | |
26 | * Rohan Puri <rohan.puri15@gmail.com> | |
27 | * Brian Behlendorf <behlendorf1@llnl.gov> | |
28 | */ | |
29 | ||
657ce253 | 30 | #include <sys/zfs_znode.h> |
ebe7e575 BB |
31 | #include <sys/zfs_vfsops.h> |
32 | #include <sys/zfs_vnops.h> | |
ebe7e575 BB |
33 | #include <sys/zfs_ctldir.h> |
34 | #include <sys/zpl.h> | |
f6097399 AW |
35 | #include <sys/dmu.h> |
36 | #include <sys/dsl_dataset.h> | |
37 | #include <sys/zap.h> | |
ebe7e575 BB |
38 | |
39 | /* | |
40 | * Common open routine. Disallow any write access. | |
41 | */ | |
ebe7e575 BB |
42 | static int |
43 | zpl_common_open(struct inode *ip, struct file *filp) | |
44 | { | |
43e8f6e3 | 45 | if (blk_mode_is_open_write(filp->f_mode)) |
ebe7e575 BB |
46 | return (-EACCES); |
47 | ||
d1d7e268 | 48 | return (generic_file_open(ip, filp)); |
ebe7e575 BB |
49 | } |
50 | ||
ebe7e575 BB |
51 | /* |
52 | * Get root directory contents. | |
53 | */ | |
54 | static int | |
9464b959 | 55 | zpl_root_iterate(struct file *filp, zpl_dir_context_t *ctx) |
ebe7e575 | 56 | { |
0037b49e | 57 | zfsvfs_t *zfsvfs = ITOZSB(file_inode(filp)); |
ebe7e575 BB |
58 | int error = 0; |
59 | ||
768eaced CC |
60 | if ((error = zpl_enter(zfsvfs, FTAG)) != 0) |
61 | return (error); | |
ebe7e575 | 62 | |
9464b959 | 63 | if (!zpl_dir_emit_dots(filp, ctx)) |
0f37d0c8 | 64 | goto out; |
ebe7e575 | 65 | |
0f37d0c8 | 66 | if (ctx->pos == 2) { |
9464b959 BB |
67 | if (!zpl_dir_emit(ctx, ZFS_SNAPDIR_NAME, |
68 | strlen(ZFS_SNAPDIR_NAME), ZFSCTL_INO_SNAPDIR, DT_DIR)) | |
ebe7e575 BB |
69 | goto out; |
70 | ||
0f37d0c8 RY |
71 | ctx->pos++; |
72 | } | |
ebe7e575 | 73 | |
0f37d0c8 | 74 | if (ctx->pos == 3) { |
9464b959 BB |
75 | if (!zpl_dir_emit(ctx, ZFS_SHAREDIR_NAME, |
76 | strlen(ZFS_SHAREDIR_NAME), ZFSCTL_INO_SHARES, DT_DIR)) | |
ebe7e575 BB |
77 | goto out; |
78 | ||
0f37d0c8 | 79 | ctx->pos++; |
ebe7e575 BB |
80 | } |
81 | out: | |
768eaced | 82 | zpl_exit(zfsvfs, FTAG); |
ebe7e575 BB |
83 | |
84 | return (error); | |
85 | } | |
86 | ||
9baaa7de | 87 | #if !defined(HAVE_VFS_ITERATE) && !defined(HAVE_VFS_ITERATE_SHARED) |
0f37d0c8 RY |
88 | static int |
89 | zpl_root_readdir(struct file *filp, void *dirent, filldir_t filldir) | |
90 | { | |
9464b959 BB |
91 | zpl_dir_context_t ctx = |
92 | ZPL_DIR_CONTEXT_INIT(dirent, filldir, filp->f_pos); | |
0f37d0c8 RY |
93 | int error; |
94 | ||
95 | error = zpl_root_iterate(filp, &ctx); | |
96 | filp->f_pos = ctx.pos; | |
97 | ||
98 | return (error); | |
99 | } | |
9464b959 | 100 | #endif /* !HAVE_VFS_ITERATE && !HAVE_VFS_ITERATE_SHARED */ |
0f37d0c8 | 101 | |
ebe7e575 BB |
102 | /* |
103 | * Get root directory attributes. | |
104 | */ | |
ebe7e575 | 105 | static int |
d4dc53da YY |
106 | #ifdef HAVE_IDMAP_IOPS_GETATTR |
107 | zpl_root_getattr_impl(struct mnt_idmap *user_ns, | |
108 | const struct path *path, struct kstat *stat, u32 request_mask, | |
109 | unsigned int query_flags) | |
110 | #elif defined(HAVE_USERNS_IOPS_GETATTR) | |
e2a82961 CK |
111 | zpl_root_getattr_impl(struct user_namespace *user_ns, |
112 | const struct path *path, struct kstat *stat, u32 request_mask, | |
113 | unsigned int query_flags) | |
114 | #else | |
a3478c07 OF |
115 | zpl_root_getattr_impl(const struct path *path, struct kstat *stat, |
116 | u32 request_mask, unsigned int query_flags) | |
e2a82961 | 117 | #endif |
ebe7e575 | 118 | { |
ef70eff1 | 119 | (void) request_mask, (void) query_flags; |
2946a1a1 BB |
120 | struct inode *ip = path->dentry->d_inode; |
121 | ||
d4dc53da | 122 | #if (defined(HAVE_USERNS_IOPS_GETATTR) || defined(HAVE_IDMAP_IOPS_GETATTR)) |
ef70eff1 | 123 | #ifdef HAVE_GENERIC_FILLATTR_USERNS |
e2a82961 | 124 | generic_fillattr(user_ns, ip, stat); |
d4dc53da YY |
125 | #elif defined(HAVE_GENERIC_FILLATTR_IDMAP) |
126 | generic_fillattr(user_ns, ip, stat); | |
ef70eff1 AZ |
127 | #else |
128 | (void) user_ns; | |
129 | #endif | |
e2a82961 | 130 | #else |
2946a1a1 | 131 | generic_fillattr(ip, stat); |
e2a82961 | 132 | #endif |
2946a1a1 | 133 | stat->atime = current_time(ip); |
ebe7e575 | 134 | |
a3478c07 | 135 | return (0); |
ebe7e575 | 136 | } |
a3478c07 | 137 | ZPL_GETATTR_WRAPPER(zpl_root_getattr); |
ebe7e575 BB |
138 | |
139 | static struct dentry * | |
8f195a90 | 140 | zpl_root_lookup(struct inode *dip, struct dentry *dentry, unsigned int flags) |
ebe7e575 BB |
141 | { |
142 | cred_t *cr = CRED(); | |
143 | struct inode *ip; | |
144 | int error; | |
145 | ||
146 | crhold(cr); | |
147 | error = -zfsctl_root_lookup(dip, dname(dentry), &ip, 0, cr, NULL, NULL); | |
148 | ASSERT3S(error, <=, 0); | |
149 | crfree(cr); | |
150 | ||
151 | if (error) { | |
152 | if (error == -ENOENT) | |
d1d7e268 | 153 | return (d_splice_alias(NULL, dentry)); |
ebe7e575 | 154 | else |
d1d7e268 | 155 | return (ERR_PTR(error)); |
ebe7e575 BB |
156 | } |
157 | ||
d1d7e268 | 158 | return (d_splice_alias(ip, dentry)); |
ebe7e575 BB |
159 | } |
160 | ||
161 | /* | |
162 | * The '.zfs' control directory file and inode operations. | |
163 | */ | |
164 | const struct file_operations zpl_fops_root = { | |
165 | .open = zpl_common_open, | |
166 | .llseek = generic_file_llseek, | |
167 | .read = generic_read_dir, | |
9baaa7de CC |
168 | #ifdef HAVE_VFS_ITERATE_SHARED |
169 | .iterate_shared = zpl_root_iterate, | |
170 | #elif defined(HAVE_VFS_ITERATE) | |
0f37d0c8 RY |
171 | .iterate = zpl_root_iterate, |
172 | #else | |
ebe7e575 | 173 | .readdir = zpl_root_readdir, |
0f37d0c8 | 174 | #endif |
ebe7e575 BB |
175 | }; |
176 | ||
177 | const struct inode_operations zpl_ops_root = { | |
178 | .lookup = zpl_root_lookup, | |
179 | .getattr = zpl_root_getattr, | |
180 | }; | |
181 | ||
f1a05fa1 NB |
182 | static struct vfsmount * |
183 | zpl_snapdir_automount(struct path *path) | |
184 | { | |
f1a05fa1 NB |
185 | int error; |
186 | ||
278bee93 | 187 | error = -zfsctl_snapshot_mount(path, 0); |
f1a05fa1 | 188 | if (error) |
d1d7e268 | 189 | return (ERR_PTR(error)); |
f1a05fa1 NB |
190 | |
191 | /* | |
192 | * Rather than returning the new vfsmount for the snapshot we must | |
193 | * return NULL to indicate a mount collision. This is done because | |
194 | * the user space mount calls do_add_mount() which adds the vfsmount | |
195 | * to the name space. If we returned the new mount here it would be | |
196 | * added again to the vfsmount list resulting in list corruption. | |
197 | */ | |
198 | return (NULL); | |
199 | } | |
f1a05fa1 NB |
200 | |
201 | /* | |
278bee93 BB |
202 | * Negative dentries must always be revalidated so newly created snapshots |
203 | * can be detected and automounted. Normal dentries should be kept because | |
204 | * as of the 3.18 kernel revaliding the mountpoint dentry will result in | |
205 | * the snapshot being immediately unmounted. | |
f1a05fa1 NB |
206 | */ |
207 | static int | |
208 | #ifdef HAVE_D_REVALIDATE_NAMEIDATA | |
209 | zpl_snapdir_revalidate(struct dentry *dentry, struct nameidata *i) | |
210 | #else | |
211 | zpl_snapdir_revalidate(struct dentry *dentry, unsigned int flags) | |
212 | #endif | |
213 | { | |
278bee93 | 214 | return (!!dentry->d_inode); |
f1a05fa1 NB |
215 | } |
216 | ||
a2d5643f | 217 | static dentry_operations_t zpl_dops_snapdirs = { |
f1a05fa1 NB |
218 | /* |
219 | * Auto mounting of snapshots is only supported for 2.6.37 and | |
220 | * newer kernels. Prior to this kernel the ops->follow_link() | |
221 | * callback was used as a hack to trigger the mount. The | |
222 | * resulting vfsmount was then explicitly grafted in to the | |
223 | * name space. While it might be possible to add compatibility | |
224 | * code to accomplish this it would require considerable care. | |
225 | */ | |
f1a05fa1 | 226 | .d_automount = zpl_snapdir_automount, |
f1a05fa1 NB |
227 | .d_revalidate = zpl_snapdir_revalidate, |
228 | }; | |
229 | ||
ebe7e575 | 230 | static struct dentry * |
8f195a90 YS |
231 | zpl_snapdir_lookup(struct inode *dip, struct dentry *dentry, |
232 | unsigned int flags) | |
ebe7e575 | 233 | { |
7fad6290 | 234 | fstrans_cookie_t cookie; |
ebe7e575 | 235 | cred_t *cr = CRED(); |
f1a05fa1 | 236 | struct inode *ip = NULL; |
ebe7e575 BB |
237 | int error; |
238 | ||
239 | crhold(cr); | |
7fad6290 | 240 | cookie = spl_fstrans_mark(); |
ebe7e575 BB |
241 | error = -zfsctl_snapdir_lookup(dip, dname(dentry), &ip, |
242 | 0, cr, NULL, NULL); | |
243 | ASSERT3S(error, <=, 0); | |
7fad6290 | 244 | spl_fstrans_unmark(cookie); |
ebe7e575 BB |
245 | crfree(cr); |
246 | ||
f1a05fa1 | 247 | if (error && error != -ENOENT) |
d1d7e268 | 248 | return (ERR_PTR(error)); |
ebe7e575 | 249 | |
f1a05fa1 | 250 | ASSERT(error == 0 || ip == NULL); |
bf01b5e6 | 251 | d_clear_d_op(dentry); |
f1a05fa1 | 252 | d_set_d_op(dentry, &zpl_dops_snapdirs); |
278bee93 | 253 | dentry->d_flags |= DCACHE_NEED_AUTOMOUNT; |
ebe7e575 | 254 | |
d1d7e268 | 255 | return (d_splice_alias(ip, dentry)); |
ebe7e575 BB |
256 | } |
257 | ||
ebe7e575 | 258 | static int |
9464b959 | 259 | zpl_snapdir_iterate(struct file *filp, zpl_dir_context_t *ctx) |
ebe7e575 | 260 | { |
0037b49e | 261 | zfsvfs_t *zfsvfs = ITOZSB(file_inode(filp)); |
7fad6290 | 262 | fstrans_cookie_t cookie; |
ebe7e575 | 263 | char snapname[MAXNAMELEN]; |
ebe7e575 | 264 | boolean_t case_conflict; |
7fad6290 | 265 | uint64_t id, pos; |
ebe7e575 BB |
266 | int error = 0; |
267 | ||
768eaced CC |
268 | if ((error = zpl_enter(zfsvfs, FTAG)) != 0) |
269 | return (error); | |
7fad6290 | 270 | cookie = spl_fstrans_mark(); |
ebe7e575 | 271 | |
9464b959 | 272 | if (!zpl_dir_emit_dots(filp, ctx)) |
0f37d0c8 | 273 | goto out; |
ebe7e575 | 274 | |
9bd14b87 TP |
275 | /* Start the position at 0 if it already emitted . and .. */ |
276 | pos = (ctx->pos == 2 ? 0 : ctx->pos); | |
0f37d0c8 | 277 | while (error == 0) { |
0037b49e BB |
278 | dsl_pool_config_enter(dmu_objset_pool(zfsvfs->z_os), FTAG); |
279 | error = -dmu_snapshot_list_next(zfsvfs->z_os, MAXNAMELEN, | |
7fad6290 | 280 | snapname, &id, &pos, &case_conflict); |
0037b49e | 281 | dsl_pool_config_exit(dmu_objset_pool(zfsvfs->z_os), FTAG); |
ebe7e575 BB |
282 | if (error) |
283 | goto out; | |
284 | ||
9464b959 | 285 | if (!zpl_dir_emit(ctx, snapname, strlen(snapname), |
0f37d0c8 RY |
286 | ZFSCTL_INO_SHARES - id, DT_DIR)) |
287 | goto out; | |
5f15fa22 | 288 | |
7fad6290 | 289 | ctx->pos = pos; |
ebe7e575 BB |
290 | } |
291 | out: | |
7fad6290 | 292 | spl_fstrans_unmark(cookie); |
768eaced | 293 | zpl_exit(zfsvfs, FTAG); |
ebe7e575 BB |
294 | |
295 | if (error == -ENOENT) | |
296 | return (0); | |
297 | ||
298 | return (error); | |
299 | } | |
300 | ||
9baaa7de | 301 | #if !defined(HAVE_VFS_ITERATE) && !defined(HAVE_VFS_ITERATE_SHARED) |
0f37d0c8 RY |
302 | static int |
303 | zpl_snapdir_readdir(struct file *filp, void *dirent, filldir_t filldir) | |
304 | { | |
9464b959 BB |
305 | zpl_dir_context_t ctx = |
306 | ZPL_DIR_CONTEXT_INIT(dirent, filldir, filp->f_pos); | |
0f37d0c8 RY |
307 | int error; |
308 | ||
309 | error = zpl_snapdir_iterate(filp, &ctx); | |
310 | filp->f_pos = ctx.pos; | |
311 | ||
312 | return (error); | |
313 | } | |
9464b959 | 314 | #endif /* !HAVE_VFS_ITERATE && !HAVE_VFS_ITERATE_SHARED */ |
0f37d0c8 | 315 | |
b8d9e264 | 316 | static int |
e2a82961 CK |
317 | #ifdef HAVE_IOPS_RENAME_USERNS |
318 | zpl_snapdir_rename2(struct user_namespace *user_ns, struct inode *sdip, | |
319 | struct dentry *sdentry, struct inode *tdip, struct dentry *tdentry, | |
320 | unsigned int flags) | |
d4dc53da YY |
321 | #elif defined(HAVE_IOPS_RENAME_IDMAP) |
322 | zpl_snapdir_rename2(struct mnt_idmap *user_ns, struct inode *sdip, | |
323 | struct dentry *sdentry, struct inode *tdip, struct dentry *tdentry, | |
324 | unsigned int flags) | |
e2a82961 | 325 | #else |
b8d9e264 CC |
326 | zpl_snapdir_rename2(struct inode *sdip, struct dentry *sdentry, |
327 | struct inode *tdip, struct dentry *tdentry, unsigned int flags) | |
e2a82961 | 328 | #endif |
ebe7e575 BB |
329 | { |
330 | cred_t *cr = CRED(); | |
331 | int error; | |
332 | ||
b8d9e264 CC |
333 | /* We probably don't want to support renameat2(2) in ctldir */ |
334 | if (flags) | |
335 | return (-EINVAL); | |
336 | ||
ebe7e575 BB |
337 | crhold(cr); |
338 | error = -zfsctl_snapdir_rename(sdip, dname(sdentry), | |
339 | tdip, dname(tdentry), cr, 0); | |
340 | ASSERT3S(error, <=, 0); | |
341 | crfree(cr); | |
342 | ||
343 | return (error); | |
344 | } | |
345 | ||
d4dc53da YY |
346 | #if (!defined(HAVE_RENAME_WANTS_FLAGS) && \ |
347 | !defined(HAVE_IOPS_RENAME_USERNS) && \ | |
348 | !defined(HAVE_IOPS_RENAME_IDMAP)) | |
b8d9e264 CC |
349 | static int |
350 | zpl_snapdir_rename(struct inode *sdip, struct dentry *sdentry, | |
351 | struct inode *tdip, struct dentry *tdentry) | |
352 | { | |
353 | return (zpl_snapdir_rename2(sdip, sdentry, tdip, tdentry, 0)); | |
354 | } | |
355 | #endif | |
356 | ||
ebe7e575 BB |
357 | static int |
358 | zpl_snapdir_rmdir(struct inode *dip, struct dentry *dentry) | |
359 | { | |
360 | cred_t *cr = CRED(); | |
361 | int error; | |
362 | ||
363 | crhold(cr); | |
364 | error = -zfsctl_snapdir_remove(dip, dname(dentry), cr, 0); | |
365 | ASSERT3S(error, <=, 0); | |
366 | crfree(cr); | |
367 | ||
368 | return (error); | |
369 | } | |
370 | ||
371 | static int | |
e2a82961 CK |
372 | #ifdef HAVE_IOPS_MKDIR_USERNS |
373 | zpl_snapdir_mkdir(struct user_namespace *user_ns, struct inode *dip, | |
374 | struct dentry *dentry, umode_t mode) | |
d4dc53da YY |
375 | #elif defined(HAVE_IOPS_MKDIR_IDMAP) |
376 | zpl_snapdir_mkdir(struct mnt_idmap *user_ns, struct inode *dip, | |
377 | struct dentry *dentry, umode_t mode) | |
e2a82961 | 378 | #else |
066e8252 | 379 | zpl_snapdir_mkdir(struct inode *dip, struct dentry *dentry, umode_t mode) |
e2a82961 | 380 | #endif |
ebe7e575 BB |
381 | { |
382 | cred_t *cr = CRED(); | |
383 | vattr_t *vap; | |
384 | struct inode *ip; | |
385 | int error; | |
386 | ||
387 | crhold(cr); | |
d1d7e268 | 388 | vap = kmem_zalloc(sizeof (vattr_t), KM_SLEEP); |
d4dc53da | 389 | #if (defined(HAVE_IOPS_MKDIR_USERNS) || defined(HAVE_IOPS_MKDIR_IDMAP)) |
2a068a13 YY |
390 | zpl_vap_init(vap, dip, mode | S_IFDIR, cr, user_ns); |
391 | #else | |
d4dc53da | 392 | zpl_vap_init(vap, dip, mode | S_IFDIR, cr, zfs_init_idmap); |
2a068a13 | 393 | #endif |
ebe7e575 BB |
394 | |
395 | error = -zfsctl_snapdir_mkdir(dip, dname(dentry), vap, &ip, cr, 0); | |
396 | if (error == 0) { | |
bf01b5e6 | 397 | d_clear_d_op(dentry); |
f1a05fa1 | 398 | d_set_d_op(dentry, &zpl_dops_snapdirs); |
ebe7e575 BB |
399 | d_instantiate(dentry, ip); |
400 | } | |
401 | ||
d1d7e268 | 402 | kmem_free(vap, sizeof (vattr_t)); |
ebe7e575 BB |
403 | ASSERT3S(error, <=, 0); |
404 | crfree(cr); | |
405 | ||
406 | return (error); | |
407 | } | |
408 | ||
ebe7e575 BB |
409 | /* |
410 | * Get snapshot directory attributes. | |
411 | */ | |
ebe7e575 | 412 | static int |
d4dc53da YY |
413 | #ifdef HAVE_IDMAP_IOPS_GETATTR |
414 | zpl_snapdir_getattr_impl(struct mnt_idmap *user_ns, | |
415 | const struct path *path, struct kstat *stat, u32 request_mask, | |
416 | unsigned int query_flags) | |
417 | #elif defined(HAVE_USERNS_IOPS_GETATTR) | |
e2a82961 CK |
418 | zpl_snapdir_getattr_impl(struct user_namespace *user_ns, |
419 | const struct path *path, struct kstat *stat, u32 request_mask, | |
420 | unsigned int query_flags) | |
421 | #else | |
a3478c07 OF |
422 | zpl_snapdir_getattr_impl(const struct path *path, struct kstat *stat, |
423 | u32 request_mask, unsigned int query_flags) | |
e2a82961 | 424 | #endif |
ebe7e575 | 425 | { |
ef70eff1 | 426 | (void) request_mask, (void) query_flags; |
2946a1a1 BB |
427 | struct inode *ip = path->dentry->d_inode; |
428 | zfsvfs_t *zfsvfs = ITOZSB(ip); | |
768eaced | 429 | int error; |
ebe7e575 | 430 | |
768eaced CC |
431 | if ((error = zpl_enter(zfsvfs, FTAG)) != 0) |
432 | return (error); | |
d4dc53da | 433 | #if (defined(HAVE_USERNS_IOPS_GETATTR) || defined(HAVE_IDMAP_IOPS_GETATTR)) |
ef70eff1 | 434 | #ifdef HAVE_GENERIC_FILLATTR_USERNS |
e2a82961 | 435 | generic_fillattr(user_ns, ip, stat); |
d4dc53da YY |
436 | #elif defined(HAVE_GENERIC_FILLATTR_IDMAP) |
437 | generic_fillattr(user_ns, ip, stat); | |
ef70eff1 AZ |
438 | #else |
439 | (void) user_ns; | |
440 | #endif | |
e2a82961 | 441 | #else |
2946a1a1 | 442 | generic_fillattr(ip, stat); |
e2a82961 | 443 | #endif |
a3478c07 | 444 | |
278bee93 | 445 | stat->nlink = stat->size = 2; |
f6097399 AW |
446 | |
447 | dsl_dataset_t *ds = dmu_objset_ds(zfsvfs->z_os); | |
448 | if (dsl_dataset_phys(ds)->ds_snapnames_zapobj != 0) { | |
449 | uint64_t snap_count; | |
450 | int err = zap_count( | |
451 | dmu_objset_pool(ds->ds_objset)->dp_meta_objset, | |
452 | dsl_dataset_phys(ds)->ds_snapnames_zapobj, &snap_count); | |
453 | if (err != 0) { | |
768eaced | 454 | zpl_exit(zfsvfs, FTAG); |
f6097399 AW |
455 | return (-err); |
456 | } | |
457 | stat->nlink += snap_count; | |
458 | } | |
459 | ||
0037b49e | 460 | stat->ctime = stat->mtime = dmu_objset_snap_cmtime(zfsvfs->z_os); |
2946a1a1 | 461 | stat->atime = current_time(ip); |
768eaced | 462 | zpl_exit(zfsvfs, FTAG); |
ebe7e575 | 463 | |
a3478c07 | 464 | return (0); |
ebe7e575 | 465 | } |
a3478c07 | 466 | ZPL_GETATTR_WRAPPER(zpl_snapdir_getattr); |
ebe7e575 BB |
467 | |
468 | /* | |
469 | * The '.zfs/snapshot' directory file operations. These mainly control | |
470 | * generating the list of available snapshots when doing an 'ls' in the | |
471 | * directory. See zpl_snapdir_readdir(). | |
472 | */ | |
473 | const struct file_operations zpl_fops_snapdir = { | |
474 | .open = zpl_common_open, | |
475 | .llseek = generic_file_llseek, | |
476 | .read = generic_read_dir, | |
9baaa7de CC |
477 | #ifdef HAVE_VFS_ITERATE_SHARED |
478 | .iterate_shared = zpl_snapdir_iterate, | |
479 | #elif defined(HAVE_VFS_ITERATE) | |
0f37d0c8 RY |
480 | .iterate = zpl_snapdir_iterate, |
481 | #else | |
ebe7e575 | 482 | .readdir = zpl_snapdir_readdir, |
0f37d0c8 RY |
483 | #endif |
484 | ||
ebe7e575 BB |
485 | }; |
486 | ||
487 | /* | |
488 | * The '.zfs/snapshot' directory inode operations. These mainly control | |
489 | * creating an inode for a snapshot directory and initializing the needed | |
490 | * infrastructure to automount the snapshot. See zpl_snapdir_lookup(). | |
491 | */ | |
492 | const struct inode_operations zpl_ops_snapdir = { | |
493 | .lookup = zpl_snapdir_lookup, | |
494 | .getattr = zpl_snapdir_getattr, | |
d4dc53da YY |
495 | #if (defined(HAVE_RENAME_WANTS_FLAGS) || \ |
496 | defined(HAVE_IOPS_RENAME_USERNS) || \ | |
497 | defined(HAVE_IOPS_RENAME_IDMAP)) | |
b8d9e264 CC |
498 | .rename = zpl_snapdir_rename2, |
499 | #else | |
ebe7e575 | 500 | .rename = zpl_snapdir_rename, |
b8d9e264 | 501 | #endif |
ebe7e575 BB |
502 | .rmdir = zpl_snapdir_rmdir, |
503 | .mkdir = zpl_snapdir_mkdir, | |
504 | }; | |
505 | ||
ebe7e575 | 506 | static struct dentry * |
8f195a90 YS |
507 | zpl_shares_lookup(struct inode *dip, struct dentry *dentry, |
508 | unsigned int flags) | |
ebe7e575 | 509 | { |
7fad6290 | 510 | fstrans_cookie_t cookie; |
ebe7e575 BB |
511 | cred_t *cr = CRED(); |
512 | struct inode *ip = NULL; | |
513 | int error; | |
514 | ||
515 | crhold(cr); | |
7fad6290 | 516 | cookie = spl_fstrans_mark(); |
ebe7e575 BB |
517 | error = -zfsctl_shares_lookup(dip, dname(dentry), &ip, |
518 | 0, cr, NULL, NULL); | |
519 | ASSERT3S(error, <=, 0); | |
7fad6290 | 520 | spl_fstrans_unmark(cookie); |
ebe7e575 BB |
521 | crfree(cr); |
522 | ||
523 | if (error) { | |
524 | if (error == -ENOENT) | |
d1d7e268 | 525 | return (d_splice_alias(NULL, dentry)); |
ebe7e575 | 526 | else |
d1d7e268 | 527 | return (ERR_PTR(error)); |
ebe7e575 BB |
528 | } |
529 | ||
d1d7e268 | 530 | return (d_splice_alias(ip, dentry)); |
ebe7e575 BB |
531 | } |
532 | ||
ebe7e575 | 533 | static int |
9464b959 | 534 | zpl_shares_iterate(struct file *filp, zpl_dir_context_t *ctx) |
ebe7e575 | 535 | { |
7fad6290 | 536 | fstrans_cookie_t cookie; |
ebe7e575 | 537 | cred_t *cr = CRED(); |
0037b49e | 538 | zfsvfs_t *zfsvfs = ITOZSB(file_inode(filp)); |
ebe7e575 | 539 | znode_t *dzp; |
0f37d0c8 | 540 | int error = 0; |
ebe7e575 | 541 | |
768eaced CC |
542 | if ((error = zpl_enter(zfsvfs, FTAG)) != 0) |
543 | return (error); | |
7fad6290 | 544 | cookie = spl_fstrans_mark(); |
ebe7e575 | 545 | |
0037b49e | 546 | if (zfsvfs->z_shares_dir == 0) { |
9464b959 | 547 | zpl_dir_emit_dots(filp, ctx); |
0f37d0c8 | 548 | goto out; |
ebe7e575 BB |
549 | } |
550 | ||
0037b49e | 551 | error = -zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &dzp); |
0f37d0c8 RY |
552 | if (error) |
553 | goto out; | |
ebe7e575 BB |
554 | |
555 | crhold(cr); | |
0f37d0c8 | 556 | error = -zfs_readdir(ZTOI(dzp), ctx, cr); |
ebe7e575 BB |
557 | crfree(cr); |
558 | ||
559 | iput(ZTOI(dzp)); | |
0f37d0c8 | 560 | out: |
7fad6290 | 561 | spl_fstrans_unmark(cookie); |
768eaced | 562 | zpl_exit(zfsvfs, FTAG); |
ebe7e575 BB |
563 | ASSERT3S(error, <=, 0); |
564 | ||
565 | return (error); | |
566 | } | |
567 | ||
9baaa7de | 568 | #if !defined(HAVE_VFS_ITERATE) && !defined(HAVE_VFS_ITERATE_SHARED) |
0f37d0c8 RY |
569 | static int |
570 | zpl_shares_readdir(struct file *filp, void *dirent, filldir_t filldir) | |
571 | { | |
9464b959 BB |
572 | zpl_dir_context_t ctx = |
573 | ZPL_DIR_CONTEXT_INIT(dirent, filldir, filp->f_pos); | |
0f37d0c8 RY |
574 | int error; |
575 | ||
576 | error = zpl_shares_iterate(filp, &ctx); | |
577 | filp->f_pos = ctx.pos; | |
578 | ||
579 | return (error); | |
580 | } | |
9464b959 | 581 | #endif /* !HAVE_VFS_ITERATE && !HAVE_VFS_ITERATE_SHARED */ |
0f37d0c8 | 582 | |
ebe7e575 | 583 | static int |
e2a82961 CK |
584 | #ifdef HAVE_USERNS_IOPS_GETATTR |
585 | zpl_shares_getattr_impl(struct user_namespace *user_ns, | |
586 | const struct path *path, struct kstat *stat, u32 request_mask, | |
587 | unsigned int query_flags) | |
d4dc53da YY |
588 | #elif defined(HAVE_IDMAP_IOPS_GETATTR) |
589 | zpl_shares_getattr_impl(struct mnt_idmap *user_ns, | |
590 | const struct path *path, struct kstat *stat, u32 request_mask, | |
591 | unsigned int query_flags) | |
e2a82961 | 592 | #else |
a3478c07 OF |
593 | zpl_shares_getattr_impl(const struct path *path, struct kstat *stat, |
594 | u32 request_mask, unsigned int query_flags) | |
e2a82961 | 595 | #endif |
ebe7e575 | 596 | { |
ef70eff1 | 597 | (void) request_mask, (void) query_flags; |
a3478c07 | 598 | struct inode *ip = path->dentry->d_inode; |
0037b49e | 599 | zfsvfs_t *zfsvfs = ITOZSB(ip); |
ebe7e575 BB |
600 | znode_t *dzp; |
601 | int error; | |
602 | ||
768eaced CC |
603 | if ((error = zpl_enter(zfsvfs, FTAG)) != 0) |
604 | return (error); | |
ebe7e575 | 605 | |
0037b49e | 606 | if (zfsvfs->z_shares_dir == 0) { |
d4dc53da | 607 | #if (defined(HAVE_USERNS_IOPS_GETATTR) || defined(HAVE_IDMAP_IOPS_GETATTR)) |
ef70eff1 | 608 | #ifdef HAVE_GENERIC_FILLATTR_USERNS |
e2a82961 | 609 | generic_fillattr(user_ns, path->dentry->d_inode, stat); |
d4dc53da YY |
610 | #elif defined(HAVE_GENERIC_FILLATTR_IDMAP) |
611 | generic_fillattr(user_ns, path->dentry->d_inode, stat); | |
ef70eff1 AZ |
612 | #else |
613 | (void) user_ns; | |
614 | #endif | |
e2a82961 | 615 | #else |
a3478c07 | 616 | generic_fillattr(path->dentry->d_inode, stat); |
e2a82961 | 617 | #endif |
ebe7e575 | 618 | stat->nlink = stat->size = 2; |
2946a1a1 | 619 | stat->atime = current_time(ip); |
768eaced | 620 | zpl_exit(zfsvfs, FTAG); |
a3478c07 | 621 | return (0); |
ebe7e575 BB |
622 | } |
623 | ||
0037b49e | 624 | error = -zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &dzp); |
90ee9ed3 | 625 | if (error == 0) { |
d4dc53da | 626 | #if (defined(HAVE_USERNS_IOPS_GETATTR) || defined(HAVE_IDMAP_IOPS_GETATTR)) |
e2a82961 CK |
627 | error = -zfs_getattr_fast(user_ns, ZTOI(dzp), stat); |
628 | #else | |
629 | error = -zfs_getattr_fast(kcred->user_ns, ZTOI(dzp), stat); | |
630 | #endif | |
90ee9ed3 BB |
631 | iput(ZTOI(dzp)); |
632 | } | |
ebe7e575 | 633 | |
768eaced | 634 | zpl_exit(zfsvfs, FTAG); |
ebe7e575 BB |
635 | ASSERT3S(error, <=, 0); |
636 | ||
637 | return (error); | |
638 | } | |
a3478c07 | 639 | ZPL_GETATTR_WRAPPER(zpl_shares_getattr); |
ebe7e575 BB |
640 | |
641 | /* | |
642 | * The '.zfs/shares' directory file operations. | |
643 | */ | |
644 | const struct file_operations zpl_fops_shares = { | |
645 | .open = zpl_common_open, | |
646 | .llseek = generic_file_llseek, | |
647 | .read = generic_read_dir, | |
9baaa7de CC |
648 | #ifdef HAVE_VFS_ITERATE_SHARED |
649 | .iterate_shared = zpl_shares_iterate, | |
650 | #elif defined(HAVE_VFS_ITERATE) | |
0f37d0c8 RY |
651 | .iterate = zpl_shares_iterate, |
652 | #else | |
ebe7e575 | 653 | .readdir = zpl_shares_readdir, |
0f37d0c8 RY |
654 | #endif |
655 | ||
ebe7e575 BB |
656 | }; |
657 | ||
658 | /* | |
659 | * The '.zfs/shares' directory inode operations. | |
660 | */ | |
661 | const struct inode_operations zpl_ops_shares = { | |
662 | .lookup = zpl_shares_lookup, | |
663 | .getattr = zpl_shares_getattr, | |
664 | }; |