]>
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 | |
9 | * or http://www.opensolaris.org/os/licensing. | |
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 | ||
30 | #include <sys/zfs_vfsops.h> | |
31 | #include <sys/zfs_vnops.h> | |
32 | #include <sys/zfs_znode.h> | |
33 | #include <sys/zfs_ctldir.h> | |
34 | #include <sys/zpl.h> | |
35 | ||
36 | /* | |
37 | * Common open routine. Disallow any write access. | |
38 | */ | |
39 | /* ARGSUSED */ | |
40 | static int | |
41 | zpl_common_open(struct inode *ip, struct file *filp) | |
42 | { | |
43 | if (filp->f_mode & FMODE_WRITE) | |
44 | return (-EACCES); | |
45 | ||
46 | return generic_file_open(ip, filp); | |
47 | } | |
48 | ||
49 | static int | |
50 | zpl_common_readdir(struct file *filp, void *dirent, filldir_t filldir) | |
51 | { | |
52 | struct dentry *dentry = filp->f_path.dentry; | |
53 | struct inode *ip = dentry->d_inode; | |
54 | int error = 0; | |
55 | ||
56 | switch (filp->f_pos) { | |
57 | case 0: | |
58 | error = filldir(dirent, ".", 1, 0, ip->i_ino, DT_DIR); | |
59 | if (error) | |
60 | break; | |
61 | ||
62 | filp->f_pos++; | |
63 | /* fall-thru */ | |
64 | case 1: | |
65 | error = filldir(dirent, "..", 2, 1, parent_ino(dentry), DT_DIR); | |
66 | if (error) | |
67 | break; | |
68 | ||
69 | filp->f_pos++; | |
70 | /* fall-thru */ | |
71 | default: | |
72 | break; | |
73 | } | |
74 | ||
75 | return (error); | |
76 | } | |
77 | ||
78 | /* | |
79 | * Get root directory contents. | |
80 | */ | |
81 | static int | |
82 | zpl_root_readdir(struct file *filp, void *dirent, filldir_t filldir) | |
83 | { | |
84 | struct dentry *dentry = filp->f_path.dentry; | |
85 | struct inode *ip = dentry->d_inode; | |
86 | zfs_sb_t *zsb = ITOZSB(ip); | |
87 | int error = 0; | |
88 | ||
89 | ZFS_ENTER(zsb); | |
90 | ||
91 | switch (filp->f_pos) { | |
92 | case 0: | |
93 | error = filldir(dirent, ".", 1, 0, ip->i_ino, DT_DIR); | |
94 | if (error) | |
95 | goto out; | |
96 | ||
97 | filp->f_pos++; | |
98 | /* fall-thru */ | |
99 | case 1: | |
100 | error = filldir(dirent, "..", 2, 1, parent_ino(dentry), DT_DIR); | |
101 | if (error) | |
102 | goto out; | |
103 | ||
104 | filp->f_pos++; | |
105 | /* fall-thru */ | |
106 | case 2: | |
107 | error = filldir(dirent, ZFS_SNAPDIR_NAME, | |
108 | strlen(ZFS_SNAPDIR_NAME), 2, ZFSCTL_INO_SNAPDIR, DT_DIR); | |
109 | if (error) | |
110 | goto out; | |
111 | ||
112 | filp->f_pos++; | |
113 | /* fall-thru */ | |
114 | case 3: | |
115 | error = filldir(dirent, ZFS_SHAREDIR_NAME, | |
116 | strlen(ZFS_SHAREDIR_NAME), 3, ZFSCTL_INO_SHARES, DT_DIR); | |
117 | if (error) | |
118 | goto out; | |
119 | ||
120 | filp->f_pos++; | |
121 | /* fall-thru */ | |
122 | } | |
123 | out: | |
124 | ZFS_EXIT(zsb); | |
125 | ||
126 | return (error); | |
127 | } | |
128 | ||
129 | /* | |
130 | * Get root directory attributes. | |
131 | */ | |
132 | /* ARGSUSED */ | |
133 | static int | |
134 | zpl_root_getattr(struct vfsmount *mnt, struct dentry *dentry, | |
135 | struct kstat *stat) | |
136 | { | |
137 | int error; | |
138 | ||
139 | error = simple_getattr(mnt, dentry, stat); | |
140 | stat->atime = CURRENT_TIME; | |
141 | ||
142 | return (error); | |
143 | } | |
144 | ||
145 | static struct dentry * | |
8f195a90 | 146 | #ifdef HAVE_LOOKUP_NAMEIDATA |
ebe7e575 | 147 | zpl_root_lookup(struct inode *dip, struct dentry *dentry, struct nameidata *nd) |
8f195a90 YS |
148 | #else |
149 | zpl_root_lookup(struct inode *dip, struct dentry *dentry, unsigned int flags) | |
150 | #endif | |
ebe7e575 BB |
151 | { |
152 | cred_t *cr = CRED(); | |
153 | struct inode *ip; | |
154 | int error; | |
155 | ||
156 | crhold(cr); | |
157 | error = -zfsctl_root_lookup(dip, dname(dentry), &ip, 0, cr, NULL, NULL); | |
158 | ASSERT3S(error, <=, 0); | |
159 | crfree(cr); | |
160 | ||
161 | if (error) { | |
162 | if (error == -ENOENT) | |
163 | return d_splice_alias(NULL, dentry); | |
164 | else | |
165 | return ERR_PTR(error); | |
166 | } | |
167 | ||
168 | return d_splice_alias(ip, dentry); | |
169 | } | |
170 | ||
171 | /* | |
172 | * The '.zfs' control directory file and inode operations. | |
173 | */ | |
174 | const struct file_operations zpl_fops_root = { | |
175 | .open = zpl_common_open, | |
176 | .llseek = generic_file_llseek, | |
177 | .read = generic_read_dir, | |
178 | .readdir = zpl_root_readdir, | |
179 | }; | |
180 | ||
181 | const struct inode_operations zpl_ops_root = { | |
182 | .lookup = zpl_root_lookup, | |
183 | .getattr = zpl_root_getattr, | |
184 | }; | |
185 | ||
186 | static struct dentry * | |
8f195a90 | 187 | #ifdef HAVE_LOOKUP_NAMEIDATA |
ebe7e575 BB |
188 | zpl_snapdir_lookup(struct inode *dip, struct dentry *dentry, |
189 | struct nameidata *nd) | |
8f195a90 YS |
190 | #else |
191 | zpl_snapdir_lookup(struct inode *dip, struct dentry *dentry, | |
192 | unsigned int flags) | |
193 | #endif | |
194 | ||
ebe7e575 BB |
195 | { |
196 | cred_t *cr = CRED(); | |
197 | struct inode *ip; | |
198 | int error; | |
199 | ||
200 | crhold(cr); | |
201 | error = -zfsctl_snapdir_lookup(dip, dname(dentry), &ip, | |
202 | 0, cr, NULL, NULL); | |
203 | ASSERT3S(error, <=, 0); | |
204 | crfree(cr); | |
205 | ||
206 | if (error) { | |
207 | if (error == -ENOENT) | |
208 | return d_splice_alias(NULL, dentry); | |
209 | else | |
210 | return ERR_PTR(error); | |
211 | } | |
212 | ||
213 | /* | |
214 | * Auto mounting of snapshots is only supported for 2.6.37 and | |
215 | * newer kernels. Prior to this kernel the ops->follow_link() | |
216 | * callback was used as a hack to trigger the mount. The | |
217 | * resulting vfsmount was then explicitly grafted in to the | |
218 | * name space. While it might be possible to add compatibility | |
219 | * code to accomplish this it would require considerable care. | |
220 | */ | |
221 | #ifdef HAVE_AUTOMOUNT | |
222 | dentry->d_op = &zpl_dops_snapdirs; | |
223 | #endif /* HAVE_AUTOMOUNT */ | |
224 | ||
225 | return d_splice_alias(ip, dentry); | |
226 | } | |
227 | ||
228 | /* ARGSUSED */ | |
229 | static int | |
230 | zpl_snapdir_readdir(struct file *filp, void *dirent, filldir_t filldir) | |
231 | { | |
232 | struct dentry *dentry = filp->f_path.dentry; | |
233 | struct inode *dip = dentry->d_inode; | |
234 | zfs_sb_t *zsb = ITOZSB(dip); | |
235 | char snapname[MAXNAMELEN]; | |
236 | uint64_t id, cookie; | |
237 | boolean_t case_conflict; | |
238 | int error = 0; | |
239 | ||
240 | ZFS_ENTER(zsb); | |
241 | ||
242 | cookie = filp->f_pos; | |
243 | switch (filp->f_pos) { | |
244 | case 0: | |
245 | error = filldir(dirent, ".", 1, 0, dip->i_ino, DT_DIR); | |
246 | if (error) | |
247 | goto out; | |
248 | ||
249 | filp->f_pos++; | |
250 | /* fall-thru */ | |
251 | case 1: | |
252 | error = filldir(dirent, "..", 2, 1, parent_ino(dentry), DT_DIR); | |
253 | if (error) | |
254 | goto out; | |
255 | ||
256 | filp->f_pos++; | |
257 | /* fall-thru */ | |
258 | default: | |
259 | while (error == 0) { | |
260 | error = -dmu_snapshot_list_next(zsb->z_os, MAXNAMELEN, | |
261 | snapname, &id, &cookie, &case_conflict); | |
262 | if (error) | |
263 | goto out; | |
264 | ||
265 | error = filldir(dirent, snapname, strlen(snapname), | |
266 | filp->f_pos, ZFSCTL_INO_SHARES - id, DT_DIR); | |
267 | if (error) | |
268 | goto out; | |
269 | ||
270 | filp->f_pos = cookie; | |
271 | } | |
272 | } | |
273 | out: | |
274 | ZFS_EXIT(zsb); | |
275 | ||
276 | if (error == -ENOENT) | |
277 | return (0); | |
278 | ||
279 | return (error); | |
280 | } | |
281 | ||
282 | int | |
283 | zpl_snapdir_rename(struct inode *sdip, struct dentry *sdentry, | |
284 | struct inode *tdip, struct dentry *tdentry) | |
285 | { | |
286 | cred_t *cr = CRED(); | |
287 | int error; | |
288 | ||
289 | crhold(cr); | |
290 | error = -zfsctl_snapdir_rename(sdip, dname(sdentry), | |
291 | tdip, dname(tdentry), cr, 0); | |
292 | ASSERT3S(error, <=, 0); | |
293 | crfree(cr); | |
294 | ||
295 | return (error); | |
296 | } | |
297 | ||
298 | static int | |
299 | zpl_snapdir_rmdir(struct inode *dip, struct dentry *dentry) | |
300 | { | |
301 | cred_t *cr = CRED(); | |
302 | int error; | |
303 | ||
304 | crhold(cr); | |
305 | error = -zfsctl_snapdir_remove(dip, dname(dentry), cr, 0); | |
306 | ASSERT3S(error, <=, 0); | |
307 | crfree(cr); | |
308 | ||
309 | return (error); | |
310 | } | |
311 | ||
312 | static int | |
b39d3b9f | 313 | zpl_snapdir_mkdir(struct inode *dip, struct dentry *dentry, zpl_umode_t mode) |
ebe7e575 BB |
314 | { |
315 | cred_t *cr = CRED(); | |
316 | vattr_t *vap; | |
317 | struct inode *ip; | |
318 | int error; | |
319 | ||
320 | crhold(cr); | |
321 | vap = kmem_zalloc(sizeof(vattr_t), KM_SLEEP); | |
322 | zpl_vap_init(vap, dip, dentry, mode | S_IFDIR, cr); | |
323 | ||
324 | error = -zfsctl_snapdir_mkdir(dip, dname(dentry), vap, &ip, cr, 0); | |
325 | if (error == 0) { | |
326 | #ifdef HAVE_AUTOMOUNT | |
327 | dentry->d_op = &zpl_dops_snapdirs; | |
328 | #endif /* HAVE_AUTOMOUNT */ | |
329 | d_instantiate(dentry, ip); | |
330 | } | |
331 | ||
332 | kmem_free(vap, sizeof(vattr_t)); | |
333 | ASSERT3S(error, <=, 0); | |
334 | crfree(cr); | |
335 | ||
336 | return (error); | |
337 | } | |
338 | ||
339 | #ifdef HAVE_AUTOMOUNT | |
340 | static struct vfsmount * | |
341 | zpl_snapdir_automount(struct path *path) | |
342 | { | |
343 | struct dentry *dentry = path->dentry; | |
344 | int error; | |
345 | ||
346 | /* | |
347 | * We must briefly disable automounts for this dentry because the | |
348 | * user space mount utility will trigger another lookup on this | |
349 | * directory. That will result in zpl_snapdir_automount() being | |
350 | * called repeatedly. The DCACHE_NEED_AUTOMOUNT flag can be | |
351 | * safely reset once the mount completes. | |
352 | */ | |
353 | dentry->d_flags &= ~DCACHE_NEED_AUTOMOUNT; | |
354 | error = -zfsctl_mount_snapshot(path, 0); | |
355 | dentry->d_flags |= DCACHE_NEED_AUTOMOUNT; | |
356 | if (error) | |
357 | return ERR_PTR(error); | |
358 | ||
359 | /* | |
360 | * Rather than returning the new vfsmount for the snapshot we must | |
361 | * return NULL to indicate a mount collision. This is done because | |
362 | * the user space mount calls do_add_mount() which adds the vfsmount | |
363 | * to the name space. If we returned the new mount here it would be | |
364 | * added again to the vfsmount list resulting in list corruption. | |
365 | */ | |
366 | return (NULL); | |
367 | } | |
368 | #endif /* HAVE_AUTOMOUNT */ | |
369 | ||
370 | /* | |
371 | * Get snapshot directory attributes. | |
372 | */ | |
373 | /* ARGSUSED */ | |
374 | static int | |
375 | zpl_snapdir_getattr(struct vfsmount *mnt, struct dentry *dentry, | |
376 | struct kstat *stat) | |
377 | { | |
378 | zfs_sb_t *zsb = ITOZSB(dentry->d_inode); | |
379 | int error; | |
380 | ||
381 | ZFS_ENTER(zsb); | |
382 | error = simple_getattr(mnt, dentry, stat); | |
383 | stat->nlink = stat->size = avl_numnodes(&zsb->z_ctldir_snaps) + 2; | |
384 | stat->ctime = stat->mtime = dmu_objset_snap_cmtime(zsb->z_os); | |
385 | stat->atime = CURRENT_TIME; | |
386 | ZFS_EXIT(zsb); | |
387 | ||
388 | return (error); | |
389 | } | |
390 | ||
391 | /* | |
392 | * The '.zfs/snapshot' directory file operations. These mainly control | |
393 | * generating the list of available snapshots when doing an 'ls' in the | |
394 | * directory. See zpl_snapdir_readdir(). | |
395 | */ | |
396 | const struct file_operations zpl_fops_snapdir = { | |
397 | .open = zpl_common_open, | |
398 | .llseek = generic_file_llseek, | |
399 | .read = generic_read_dir, | |
400 | .readdir = zpl_snapdir_readdir, | |
401 | }; | |
402 | ||
403 | /* | |
404 | * The '.zfs/snapshot' directory inode operations. These mainly control | |
405 | * creating an inode for a snapshot directory and initializing the needed | |
406 | * infrastructure to automount the snapshot. See zpl_snapdir_lookup(). | |
407 | */ | |
408 | const struct inode_operations zpl_ops_snapdir = { | |
409 | .lookup = zpl_snapdir_lookup, | |
410 | .getattr = zpl_snapdir_getattr, | |
411 | .rename = zpl_snapdir_rename, | |
412 | .rmdir = zpl_snapdir_rmdir, | |
413 | .mkdir = zpl_snapdir_mkdir, | |
414 | }; | |
415 | ||
416 | #ifdef HAVE_AUTOMOUNT | |
417 | const struct dentry_operations zpl_dops_snapdirs = { | |
418 | .d_automount = zpl_snapdir_automount, | |
419 | }; | |
420 | #endif /* HAVE_AUTOMOUNT */ | |
421 | ||
422 | static struct dentry * | |
8f195a90 | 423 | #ifdef HAVE_LOOKUP_NAMEIDATA |
ebe7e575 BB |
424 | zpl_shares_lookup(struct inode *dip, struct dentry *dentry, |
425 | struct nameidata *nd) | |
8f195a90 YS |
426 | #else |
427 | zpl_shares_lookup(struct inode *dip, struct dentry *dentry, | |
428 | unsigned int flags) | |
429 | #endif | |
ebe7e575 BB |
430 | { |
431 | cred_t *cr = CRED(); | |
432 | struct inode *ip = NULL; | |
433 | int error; | |
434 | ||
435 | crhold(cr); | |
436 | error = -zfsctl_shares_lookup(dip, dname(dentry), &ip, | |
437 | 0, cr, NULL, NULL); | |
438 | ASSERT3S(error, <=, 0); | |
439 | crfree(cr); | |
440 | ||
441 | if (error) { | |
442 | if (error == -ENOENT) | |
443 | return d_splice_alias(NULL, dentry); | |
444 | else | |
445 | return ERR_PTR(error); | |
446 | } | |
447 | ||
448 | return d_splice_alias(ip, dentry); | |
449 | } | |
450 | ||
451 | /* ARGSUSED */ | |
452 | static int | |
453 | zpl_shares_readdir(struct file *filp, void *dirent, filldir_t filldir) | |
454 | { | |
455 | cred_t *cr = CRED(); | |
456 | struct dentry *dentry = filp->f_path.dentry; | |
457 | struct inode *ip = dentry->d_inode; | |
458 | zfs_sb_t *zsb = ITOZSB(ip); | |
459 | znode_t *dzp; | |
460 | int error; | |
461 | ||
462 | ZFS_ENTER(zsb); | |
463 | ||
464 | if (zsb->z_shares_dir == 0) { | |
465 | error = zpl_common_readdir(filp, dirent, filldir); | |
466 | ZFS_EXIT(zsb); | |
467 | return (error); | |
468 | } | |
469 | ||
470 | error = -zfs_zget(zsb, zsb->z_shares_dir, &dzp); | |
471 | if (error) { | |
472 | ZFS_EXIT(zsb); | |
473 | return (error); | |
474 | } | |
475 | ||
476 | crhold(cr); | |
477 | error = -zfs_readdir(ZTOI(dzp), dirent, filldir, &filp->f_pos, cr); | |
478 | crfree(cr); | |
479 | ||
480 | iput(ZTOI(dzp)); | |
481 | ZFS_EXIT(zsb); | |
482 | ASSERT3S(error, <=, 0); | |
483 | ||
484 | return (error); | |
485 | } | |
486 | ||
487 | /* ARGSUSED */ | |
488 | static int | |
489 | zpl_shares_getattr(struct vfsmount *mnt, struct dentry *dentry, | |
490 | struct kstat *stat) | |
491 | { | |
492 | struct inode *ip = dentry->d_inode; | |
493 | zfs_sb_t *zsb = ITOZSB(ip); | |
494 | znode_t *dzp; | |
495 | int error; | |
496 | ||
497 | ZFS_ENTER(zsb); | |
498 | ||
499 | if (zsb->z_shares_dir == 0) { | |
500 | error = simple_getattr(mnt, dentry, stat); | |
501 | stat->nlink = stat->size = 2; | |
502 | stat->atime = CURRENT_TIME; | |
503 | ZFS_EXIT(zsb); | |
504 | return (error); | |
505 | } | |
506 | ||
507 | error = -zfs_zget(zsb, zsb->z_shares_dir, &dzp); | |
508 | if (error == 0) | |
509 | error = -zfs_getattr_fast(dentry->d_inode, stat); | |
510 | ||
511 | iput(ZTOI(dzp)); | |
512 | ZFS_EXIT(zsb); | |
513 | ASSERT3S(error, <=, 0); | |
514 | ||
515 | return (error); | |
516 | } | |
517 | ||
518 | /* | |
519 | * The '.zfs/shares' directory file operations. | |
520 | */ | |
521 | const struct file_operations zpl_fops_shares = { | |
522 | .open = zpl_common_open, | |
523 | .llseek = generic_file_llseek, | |
524 | .read = generic_read_dir, | |
525 | .readdir = zpl_shares_readdir, | |
526 | }; | |
527 | ||
528 | /* | |
529 | * The '.zfs/shares' directory inode operations. | |
530 | */ | |
531 | const struct inode_operations zpl_ops_shares = { | |
532 | .lookup = zpl_shares_lookup, | |
533 | .getattr = zpl_shares_getattr, | |
534 | }; |