diff options
author | David Howells <dhowells@redhat.com> | 2018-02-06 01:26:30 -0500 |
---|---|---|
committer | David Howells <dhowells@redhat.com> | 2018-02-06 09:43:37 -0500 |
commit | 4d673da14533b32fe8d3125b5b7be4fea14e39a8 (patch) | |
tree | d0780b5603a48614696e7e9afebe5b61552c6cd1 | |
parent | 16280a15be751b9994e94c5dc944e93fa4293199 (diff) |
afs: Support the AFS dynamic root
Support the AFS dynamic root which is a pseudo-volume that doesn't connect
to any server resource, but rather is just a root directory that
dynamically creates mountpoint directories where the name of such a
directory is the name of the cell.
Such a mount can be created thus:
mount -t afs none /afs -o dyn
Dynamic root superblocks aren't shared except by bind mounts and
propagation. Cell root volumes can then be mounted by referring to them by
name, e.g.:
ls /afs/grand.central.org/
ls /afs/.grand.central.org/
The kernel will upcall to consult the DNS if the address wasn't supplied
directly.
Signed-off-by: David Howells <dhowells@redhat.com>
-rw-r--r-- | Documentation/filesystems/afs.txt | 17 | ||||
-rw-r--r-- | fs/afs/dir.c | 122 | ||||
-rw-r--r-- | fs/afs/inode.c | 48 | ||||
-rw-r--r-- | fs/afs/internal.h | 12 | ||||
-rw-r--r-- | fs/afs/mntpt.c | 20 | ||||
-rw-r--r-- | fs/afs/super.c | 132 | ||||
-rw-r--r-- | net/dns_resolver/dns_query.c | 22 |
7 files changed, 276 insertions, 97 deletions
diff --git a/Documentation/filesystems/afs.txt b/Documentation/filesystems/afs.txt index ba99b5ac4fd8..c5254f6d234d 100644 --- a/Documentation/filesystems/afs.txt +++ b/Documentation/filesystems/afs.txt | |||
@@ -7,6 +7,7 @@ Contents: | |||
7 | - Overview. | 7 | - Overview. |
8 | - Usage. | 8 | - Usage. |
9 | - Mountpoints. | 9 | - Mountpoints. |
10 | - Dynamic root. | ||
10 | - Proc filesystem. | 11 | - Proc filesystem. |
11 | - The cell database. | 12 | - The cell database. |
12 | - Security. | 13 | - Security. |
@@ -127,6 +128,22 @@ mounted on /afs in one go by doing: | |||
127 | umount /afs | 128 | umount /afs |
128 | 129 | ||
129 | 130 | ||
131 | ============ | ||
132 | DYNAMIC ROOT | ||
133 | ============ | ||
134 | |||
135 | A mount option is available to create a serverless mount that is only usable | ||
136 | for dynamic lookup. Creating such a mount can be done by, for example: | ||
137 | |||
138 | mount -t afs none /afs -o dyn | ||
139 | |||
140 | This creates a mount that just has an empty directory at the root. Attempting | ||
141 | to look up a name in this directory will cause a mountpoint to be created that | ||
142 | looks up a cell of the same name, for example: | ||
143 | |||
144 | ls /afs/grand.central.org/ | ||
145 | |||
146 | |||
130 | =============== | 147 | =============== |
131 | PROC FILESYSTEM | 148 | PROC FILESYSTEM |
132 | =============== | 149 | =============== |
diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 23c7f395d718..ba2b458b36d1 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c | |||
@@ -17,10 +17,13 @@ | |||
17 | #include <linux/pagemap.h> | 17 | #include <linux/pagemap.h> |
18 | #include <linux/ctype.h> | 18 | #include <linux/ctype.h> |
19 | #include <linux/sched.h> | 19 | #include <linux/sched.h> |
20 | #include <linux/dns_resolver.h> | ||
20 | #include "internal.h" | 21 | #include "internal.h" |
21 | 22 | ||
22 | static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, | 23 | static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, |
23 | unsigned int flags); | 24 | unsigned int flags); |
25 | static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentry, | ||
26 | unsigned int flags); | ||
24 | static int afs_dir_open(struct inode *inode, struct file *file); | 27 | static int afs_dir_open(struct inode *inode, struct file *file); |
25 | static int afs_readdir(struct file *file, struct dir_context *ctx); | 28 | static int afs_readdir(struct file *file, struct dir_context *ctx); |
26 | static int afs_d_revalidate(struct dentry *dentry, unsigned int flags); | 29 | static int afs_d_revalidate(struct dentry *dentry, unsigned int flags); |
@@ -64,6 +67,17 @@ const struct inode_operations afs_dir_inode_operations = { | |||
64 | .listxattr = afs_listxattr, | 67 | .listxattr = afs_listxattr, |
65 | }; | 68 | }; |
66 | 69 | ||
70 | const struct file_operations afs_dynroot_file_operations = { | ||
71 | .open = dcache_dir_open, | ||
72 | .release = dcache_dir_close, | ||
73 | .iterate_shared = dcache_readdir, | ||
74 | .llseek = dcache_dir_lseek, | ||
75 | }; | ||
76 | |||
77 | const struct inode_operations afs_dynroot_inode_operations = { | ||
78 | .lookup = afs_dynroot_lookup, | ||
79 | }; | ||
80 | |||
67 | const struct dentry_operations afs_fs_dentry_operations = { | 81 | const struct dentry_operations afs_fs_dentry_operations = { |
68 | .d_revalidate = afs_d_revalidate, | 82 | .d_revalidate = afs_d_revalidate, |
69 | .d_delete = afs_d_delete, | 83 | .d_delete = afs_d_delete, |
@@ -468,25 +482,58 @@ static int afs_do_lookup(struct inode *dir, struct dentry *dentry, | |||
468 | } | 482 | } |
469 | 483 | ||
470 | /* | 484 | /* |
485 | * Probe to see if a cell may exist. This prevents positive dentries from | ||
486 | * being created unnecessarily. | ||
487 | */ | ||
488 | static int afs_probe_cell_name(struct dentry *dentry) | ||
489 | { | ||
490 | struct afs_cell *cell; | ||
491 | const char *name = dentry->d_name.name; | ||
492 | size_t len = dentry->d_name.len; | ||
493 | int ret; | ||
494 | |||
495 | /* Names prefixed with a dot are R/W mounts. */ | ||
496 | if (name[0] == '.') { | ||
497 | if (len == 1) | ||
498 | return -EINVAL; | ||
499 | name++; | ||
500 | len--; | ||
501 | } | ||
502 | |||
503 | cell = afs_lookup_cell_rcu(afs_d2net(dentry), name, len); | ||
504 | if (!IS_ERR(cell)) { | ||
505 | afs_put_cell(afs_d2net(dentry), cell); | ||
506 | return 0; | ||
507 | } | ||
508 | |||
509 | ret = dns_query("afsdb", name, len, "ipv4", NULL, NULL); | ||
510 | if (ret == -ENODATA) | ||
511 | ret = -EDESTADDRREQ; | ||
512 | return ret; | ||
513 | } | ||
514 | |||
515 | /* | ||
471 | * Try to auto mount the mountpoint with pseudo directory, if the autocell | 516 | * Try to auto mount the mountpoint with pseudo directory, if the autocell |
472 | * operation is setted. | 517 | * operation is setted. |
473 | */ | 518 | */ |
474 | static struct inode *afs_try_auto_mntpt( | 519 | static struct inode *afs_try_auto_mntpt(struct dentry *dentry, |
475 | int ret, struct dentry *dentry, struct inode *dir, struct key *key, | 520 | struct inode *dir, struct afs_fid *fid) |
476 | struct afs_fid *fid) | ||
477 | { | 521 | { |
478 | const char *devname = dentry->d_name.name; | ||
479 | struct afs_vnode *vnode = AFS_FS_I(dir); | 522 | struct afs_vnode *vnode = AFS_FS_I(dir); |
480 | struct inode *inode; | 523 | struct inode *inode; |
524 | int ret = -ENOENT; | ||
481 | 525 | ||
482 | _enter("%d, %p{%pd}, {%x:%u}, %p", | 526 | _enter("%p{%pd}, {%x:%u}", |
483 | ret, dentry, dentry, vnode->fid.vid, vnode->fid.vnode, key); | 527 | dentry, dentry, vnode->fid.vid, vnode->fid.vnode); |
528 | |||
529 | if (!test_bit(AFS_VNODE_AUTOCELL, &vnode->flags)) | ||
530 | goto out; | ||
484 | 531 | ||
485 | if (ret != -ENOENT || | 532 | ret = afs_probe_cell_name(dentry); |
486 | !test_bit(AFS_VNODE_AUTOCELL, &vnode->flags)) | 533 | if (ret < 0) |
487 | goto out; | 534 | goto out; |
488 | 535 | ||
489 | inode = afs_iget_autocell(dir, devname, strlen(devname), key); | 536 | inode = afs_iget_pseudo_dir(dir->i_sb, false); |
490 | if (IS_ERR(inode)) { | 537 | if (IS_ERR(inode)) { |
491 | ret = PTR_ERR(inode); | 538 | ret = PTR_ERR(inode); |
492 | goto out; | 539 | goto out; |
@@ -545,13 +592,16 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, | |||
545 | 592 | ||
546 | ret = afs_do_lookup(dir, dentry, &fid, key); | 593 | ret = afs_do_lookup(dir, dentry, &fid, key); |
547 | if (ret < 0) { | 594 | if (ret < 0) { |
548 | inode = afs_try_auto_mntpt(ret, dentry, dir, key, &fid); | 595 | if (ret == -ENOENT) { |
549 | if (!IS_ERR(inode)) { | 596 | inode = afs_try_auto_mntpt(dentry, dir, &fid); |
550 | key_put(key); | 597 | if (!IS_ERR(inode)) { |
551 | goto success; | 598 | key_put(key); |
599 | goto success; | ||
600 | } | ||
601 | |||
602 | ret = PTR_ERR(inode); | ||
552 | } | 603 | } |
553 | 604 | ||
554 | ret = PTR_ERR(inode); | ||
555 | key_put(key); | 605 | key_put(key); |
556 | if (ret == -ENOENT) { | 606 | if (ret == -ENOENT) { |
557 | d_add(dentry, NULL); | 607 | d_add(dentry, NULL); |
@@ -583,12 +633,53 @@ success: | |||
583 | } | 633 | } |
584 | 634 | ||
585 | /* | 635 | /* |
636 | * Look up an entry in a dynroot directory. | ||
637 | */ | ||
638 | static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentry, | ||
639 | unsigned int flags) | ||
640 | { | ||
641 | struct afs_vnode *vnode; | ||
642 | struct afs_fid fid; | ||
643 | struct inode *inode; | ||
644 | int ret; | ||
645 | |||
646 | vnode = AFS_FS_I(dir); | ||
647 | |||
648 | _enter("%pd", dentry); | ||
649 | |||
650 | ASSERTCMP(d_inode(dentry), ==, NULL); | ||
651 | |||
652 | if (dentry->d_name.len >= AFSNAMEMAX) { | ||
653 | _leave(" = -ENAMETOOLONG"); | ||
654 | return ERR_PTR(-ENAMETOOLONG); | ||
655 | } | ||
656 | |||
657 | inode = afs_try_auto_mntpt(dentry, dir, &fid); | ||
658 | if (IS_ERR(inode)) { | ||
659 | ret = PTR_ERR(inode); | ||
660 | if (ret == -ENOENT) { | ||
661 | d_add(dentry, NULL); | ||
662 | _leave(" = NULL [negative]"); | ||
663 | return NULL; | ||
664 | } | ||
665 | _leave(" = %d [do]", ret); | ||
666 | return ERR_PTR(ret); | ||
667 | } | ||
668 | |||
669 | d_add(dentry, inode); | ||
670 | _leave(" = 0 { ino=%lu v=%u }", | ||
671 | d_inode(dentry)->i_ino, d_inode(dentry)->i_generation); | ||
672 | return NULL; | ||
673 | } | ||
674 | |||
675 | /* | ||
586 | * check that a dentry lookup hit has found a valid entry | 676 | * check that a dentry lookup hit has found a valid entry |
587 | * - NOTE! the hit can be a negative hit too, so we can't assume we have an | 677 | * - NOTE! the hit can be a negative hit too, so we can't assume we have an |
588 | * inode | 678 | * inode |
589 | */ | 679 | */ |
590 | static int afs_d_revalidate(struct dentry *dentry, unsigned int flags) | 680 | static int afs_d_revalidate(struct dentry *dentry, unsigned int flags) |
591 | { | 681 | { |
682 | struct afs_super_info *as = dentry->d_sb->s_fs_info; | ||
592 | struct afs_vnode *vnode, *dir; | 683 | struct afs_vnode *vnode, *dir; |
593 | struct afs_fid uninitialized_var(fid); | 684 | struct afs_fid uninitialized_var(fid); |
594 | struct dentry *parent; | 685 | struct dentry *parent; |
@@ -600,6 +691,9 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags) | |||
600 | if (flags & LOOKUP_RCU) | 691 | if (flags & LOOKUP_RCU) |
601 | return -ECHILD; | 692 | return -ECHILD; |
602 | 693 | ||
694 | if (as->dyn_root) | ||
695 | return 1; | ||
696 | |||
603 | if (d_really_is_positive(dentry)) { | 697 | if (d_really_is_positive(dentry)) { |
604 | vnode = AFS_FS_I(d_inode(dentry)); | 698 | vnode = AFS_FS_I(d_inode(dentry)); |
605 | _enter("{v={%x:%u} n=%pd fl=%lx},", | 699 | _enter("{v={%x:%u} n=%pd fl=%lx},", |
diff --git a/fs/afs/inode.c b/fs/afs/inode.c index c7f17c44c7ce..6b39d0255b72 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c | |||
@@ -147,7 +147,7 @@ int afs_iget5_test(struct inode *inode, void *opaque) | |||
147 | * | 147 | * |
148 | * These pseudo inodes don't match anything. | 148 | * These pseudo inodes don't match anything. |
149 | */ | 149 | */ |
150 | static int afs_iget5_autocell_test(struct inode *inode, void *opaque) | 150 | static int afs_iget5_pseudo_dir_test(struct inode *inode, void *opaque) |
151 | { | 151 | { |
152 | return 0; | 152 | return 0; |
153 | } | 153 | } |
@@ -169,31 +169,34 @@ static int afs_iget5_set(struct inode *inode, void *opaque) | |||
169 | } | 169 | } |
170 | 170 | ||
171 | /* | 171 | /* |
172 | * inode retrieval for autocell | 172 | * Create an inode for a dynamic root directory or an autocell dynamic |
173 | * automount dir. | ||
173 | */ | 174 | */ |
174 | struct inode *afs_iget_autocell(struct inode *dir, const char *dev_name, | 175 | struct inode *afs_iget_pseudo_dir(struct super_block *sb, bool root) |
175 | int namesz, struct key *key) | ||
176 | { | 176 | { |
177 | struct afs_iget_data data; | 177 | struct afs_iget_data data; |
178 | struct afs_super_info *as; | 178 | struct afs_super_info *as; |
179 | struct afs_vnode *vnode; | 179 | struct afs_vnode *vnode; |
180 | struct super_block *sb; | ||
181 | struct inode *inode; | 180 | struct inode *inode; |
182 | static atomic_t afs_autocell_ino; | 181 | static atomic_t afs_autocell_ino; |
183 | 182 | ||
184 | _enter("{%x:%u},%*.*s,", | 183 | _enter(""); |
185 | AFS_FS_I(dir)->fid.vid, AFS_FS_I(dir)->fid.vnode, | ||
186 | namesz, namesz, dev_name ?: ""); | ||
187 | 184 | ||
188 | sb = dir->i_sb; | ||
189 | as = sb->s_fs_info; | 185 | as = sb->s_fs_info; |
190 | data.volume = as->volume; | 186 | if (as->volume) { |
191 | data.fid.vid = as->volume->vid; | 187 | data.volume = as->volume; |
192 | data.fid.unique = 0; | 188 | data.fid.vid = as->volume->vid; |
193 | data.fid.vnode = 0; | 189 | } |
190 | if (root) { | ||
191 | data.fid.vnode = 1; | ||
192 | data.fid.unique = 1; | ||
193 | } else { | ||
194 | data.fid.vnode = atomic_inc_return(&afs_autocell_ino); | ||
195 | data.fid.unique = 0; | ||
196 | } | ||
194 | 197 | ||
195 | inode = iget5_locked(sb, atomic_inc_return(&afs_autocell_ino), | 198 | inode = iget5_locked(sb, data.fid.vnode, |
196 | afs_iget5_autocell_test, afs_iget5_set, | 199 | afs_iget5_pseudo_dir_test, afs_iget5_set, |
197 | &data); | 200 | &data); |
198 | if (!inode) { | 201 | if (!inode) { |
199 | _leave(" = -ENOMEM"); | 202 | _leave(" = -ENOMEM"); |
@@ -211,7 +214,12 @@ struct inode *afs_iget_autocell(struct inode *dir, const char *dev_name, | |||
211 | 214 | ||
212 | inode->i_size = 0; | 215 | inode->i_size = 0; |
213 | inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; | 216 | inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; |
214 | inode->i_op = &afs_autocell_inode_operations; | 217 | if (root) { |
218 | inode->i_op = &afs_dynroot_inode_operations; | ||
219 | inode->i_fop = &afs_dynroot_file_operations; | ||
220 | } else { | ||
221 | inode->i_op = &afs_autocell_inode_operations; | ||
222 | } | ||
215 | set_nlink(inode, 2); | 223 | set_nlink(inode, 2); |
216 | inode->i_uid = GLOBAL_ROOT_UID; | 224 | inode->i_uid = GLOBAL_ROOT_UID; |
217 | inode->i_gid = GLOBAL_ROOT_GID; | 225 | inode->i_gid = GLOBAL_ROOT_GID; |
@@ -223,8 +231,12 @@ struct inode *afs_iget_autocell(struct inode *dir, const char *dev_name, | |||
223 | inode->i_generation = 0; | 231 | inode->i_generation = 0; |
224 | 232 | ||
225 | set_bit(AFS_VNODE_PSEUDODIR, &vnode->flags); | 233 | set_bit(AFS_VNODE_PSEUDODIR, &vnode->flags); |
226 | set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags); | 234 | if (!root) { |
227 | inode->i_flags |= S_AUTOMOUNT | S_NOATIME; | 235 | set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags); |
236 | inode->i_flags |= S_AUTOMOUNT; | ||
237 | } | ||
238 | |||
239 | inode->i_flags |= S_NOATIME; | ||
228 | unlock_new_inode(inode); | 240 | unlock_new_inode(inode); |
229 | _leave(" = %p", inode); | 241 | _leave(" = %p", inode); |
230 | return inode; | 242 | return inode; |
diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 804d1f905622..f38d6a561a84 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h | |||
@@ -36,6 +36,7 @@ struct afs_mount_params { | |||
36 | bool rwpath; /* T if the parent should be considered R/W */ | 36 | bool rwpath; /* T if the parent should be considered R/W */ |
37 | bool force; /* T to force cell type */ | 37 | bool force; /* T to force cell type */ |
38 | bool autocell; /* T if set auto mount operation */ | 38 | bool autocell; /* T if set auto mount operation */ |
39 | bool dyn_root; /* T if dynamic root */ | ||
39 | afs_voltype_t type; /* type of volume requested */ | 40 | afs_voltype_t type; /* type of volume requested */ |
40 | int volnamesz; /* size of volume name */ | 41 | int volnamesz; /* size of volume name */ |
41 | const char *volname; /* name of volume to mount */ | 42 | const char *volname; /* name of volume to mount */ |
@@ -186,6 +187,7 @@ struct afs_super_info { | |||
186 | struct afs_net *net; /* Network namespace */ | 187 | struct afs_net *net; /* Network namespace */ |
187 | struct afs_cell *cell; /* The cell in which the volume resides */ | 188 | struct afs_cell *cell; /* The cell in which the volume resides */ |
188 | struct afs_volume *volume; /* volume record */ | 189 | struct afs_volume *volume; /* volume record */ |
190 | bool dyn_root; /* True if dynamic root */ | ||
189 | }; | 191 | }; |
190 | 192 | ||
191 | static inline struct afs_super_info *AFS_FS_S(struct super_block *sb) | 193 | static inline struct afs_super_info *AFS_FS_S(struct super_block *sb) |
@@ -634,10 +636,13 @@ extern bool afs_cm_incoming_call(struct afs_call *); | |||
634 | /* | 636 | /* |
635 | * dir.c | 637 | * dir.c |
636 | */ | 638 | */ |
637 | extern bool afs_dir_check_page(struct inode *, struct page *); | 639 | extern const struct file_operations afs_dir_file_operations; |
638 | extern const struct inode_operations afs_dir_inode_operations; | 640 | extern const struct inode_operations afs_dir_inode_operations; |
641 | extern const struct file_operations afs_dynroot_file_operations; | ||
642 | extern const struct inode_operations afs_dynroot_inode_operations; | ||
639 | extern const struct dentry_operations afs_fs_dentry_operations; | 643 | extern const struct dentry_operations afs_fs_dentry_operations; |
640 | extern const struct file_operations afs_dir_file_operations; | 644 | |
645 | extern bool afs_dir_check_page(struct inode *, struct page *); | ||
641 | 646 | ||
642 | /* | 647 | /* |
643 | * file.c | 648 | * file.c |
@@ -695,8 +700,7 @@ extern int afs_fs_get_capabilities(struct afs_net *, struct afs_server *, | |||
695 | */ | 700 | */ |
696 | extern int afs_fetch_status(struct afs_vnode *, struct key *); | 701 | extern int afs_fetch_status(struct afs_vnode *, struct key *); |
697 | extern int afs_iget5_test(struct inode *, void *); | 702 | extern int afs_iget5_test(struct inode *, void *); |
698 | extern struct inode *afs_iget_autocell(struct inode *, const char *, int, | 703 | extern struct inode *afs_iget_pseudo_dir(struct super_block *, bool); |
699 | struct key *); | ||
700 | extern struct inode *afs_iget(struct super_block *, struct key *, | 704 | extern struct inode *afs_iget(struct super_block *, struct key *, |
701 | struct afs_fid *, struct afs_file_status *, | 705 | struct afs_fid *, struct afs_file_status *, |
702 | struct afs_callback *, | 706 | struct afs_callback *, |
diff --git a/fs/afs/mntpt.c b/fs/afs/mntpt.c index 690fea9d84c3..99fd13500a97 100644 --- a/fs/afs/mntpt.c +++ b/fs/afs/mntpt.c | |||
@@ -72,7 +72,7 @@ static int afs_mntpt_open(struct inode *inode, struct file *file) | |||
72 | */ | 72 | */ |
73 | static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt) | 73 | static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt) |
74 | { | 74 | { |
75 | struct afs_super_info *super; | 75 | struct afs_super_info *as; |
76 | struct vfsmount *mnt; | 76 | struct vfsmount *mnt; |
77 | struct afs_vnode *vnode; | 77 | struct afs_vnode *vnode; |
78 | struct page *page; | 78 | struct page *page; |
@@ -104,13 +104,13 @@ static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt) | |||
104 | goto error_no_page; | 104 | goto error_no_page; |
105 | 105 | ||
106 | if (mntpt->d_name.name[0] == '.') { | 106 | if (mntpt->d_name.name[0] == '.') { |
107 | devname[0] = '#'; | 107 | devname[0] = '%'; |
108 | memcpy(devname + 1, mntpt->d_name.name, size - 1); | 108 | memcpy(devname + 1, mntpt->d_name.name + 1, size - 1); |
109 | memcpy(devname + size, afs_root_cell, | 109 | memcpy(devname + size, afs_root_cell, |
110 | sizeof(afs_root_cell)); | 110 | sizeof(afs_root_cell)); |
111 | rwpath = true; | 111 | rwpath = true; |
112 | } else { | 112 | } else { |
113 | devname[0] = '%'; | 113 | devname[0] = '#'; |
114 | memcpy(devname + 1, mntpt->d_name.name, size); | 114 | memcpy(devname + 1, mntpt->d_name.name, size); |
115 | memcpy(devname + size + 1, afs_root_cell, | 115 | memcpy(devname + size + 1, afs_root_cell, |
116 | sizeof(afs_root_cell)); | 116 | sizeof(afs_root_cell)); |
@@ -142,11 +142,13 @@ static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt) | |||
142 | } | 142 | } |
143 | 143 | ||
144 | /* work out what options we want */ | 144 | /* work out what options we want */ |
145 | super = AFS_FS_S(mntpt->d_sb); | 145 | as = AFS_FS_S(mntpt->d_sb); |
146 | memcpy(options, "cell=", 5); | 146 | if (as->cell) { |
147 | strcpy(options + 5, super->volume->cell->name); | 147 | memcpy(options, "cell=", 5); |
148 | if (super->volume->type == AFSVL_RWVOL || rwpath) | 148 | strcpy(options + 5, as->cell->name); |
149 | strcat(options, ",rwpath"); | 149 | if ((as->volume && as->volume->type == AFSVL_RWVOL) || rwpath) |
150 | strcat(options, ",rwpath"); | ||
151 | } | ||
150 | 152 | ||
151 | /* try and do the mount */ | 153 | /* try and do the mount */ |
152 | _debug("--- attempting mount %s -o %s ---", devname, options); | 154 | _debug("--- attempting mount %s -o %s ---", devname, options); |
diff --git a/fs/afs/super.c b/fs/afs/super.c index 1037dd41a622..3623c952b6ff 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c | |||
@@ -64,6 +64,7 @@ static atomic_t afs_count_active_inodes; | |||
64 | enum { | 64 | enum { |
65 | afs_no_opt, | 65 | afs_no_opt, |
66 | afs_opt_cell, | 66 | afs_opt_cell, |
67 | afs_opt_dyn, | ||
67 | afs_opt_rwpath, | 68 | afs_opt_rwpath, |
68 | afs_opt_vol, | 69 | afs_opt_vol, |
69 | afs_opt_autocell, | 70 | afs_opt_autocell, |
@@ -71,6 +72,7 @@ enum { | |||
71 | 72 | ||
72 | static const match_table_t afs_options_list = { | 73 | static const match_table_t afs_options_list = { |
73 | { afs_opt_cell, "cell=%s" }, | 74 | { afs_opt_cell, "cell=%s" }, |
75 | { afs_opt_dyn, "dyn" }, | ||
74 | { afs_opt_rwpath, "rwpath" }, | 76 | { afs_opt_rwpath, "rwpath" }, |
75 | { afs_opt_vol, "vol=%s" }, | 77 | { afs_opt_vol, "vol=%s" }, |
76 | { afs_opt_autocell, "autocell" }, | 78 | { afs_opt_autocell, "autocell" }, |
@@ -148,6 +150,11 @@ static int afs_show_devname(struct seq_file *m, struct dentry *root) | |||
148 | const char *suf = ""; | 150 | const char *suf = ""; |
149 | char pref = '%'; | 151 | char pref = '%'; |
150 | 152 | ||
153 | if (as->dyn_root) { | ||
154 | seq_puts(m, "none"); | ||
155 | return 0; | ||
156 | } | ||
157 | |||
151 | switch (volume->type) { | 158 | switch (volume->type) { |
152 | case AFSVL_RWVOL: | 159 | case AFSVL_RWVOL: |
153 | break; | 160 | break; |
@@ -171,8 +178,12 @@ static int afs_show_devname(struct seq_file *m, struct dentry *root) | |||
171 | */ | 178 | */ |
172 | static int afs_show_options(struct seq_file *m, struct dentry *root) | 179 | static int afs_show_options(struct seq_file *m, struct dentry *root) |
173 | { | 180 | { |
181 | struct afs_super_info *as = AFS_FS_S(root->d_sb); | ||
182 | |||
183 | if (as->dyn_root) | ||
184 | seq_puts(m, ",dyn"); | ||
174 | if (test_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(d_inode(root))->flags)) | 185 | if (test_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(d_inode(root))->flags)) |
175 | seq_puts(m, "autocell"); | 186 | seq_puts(m, ",autocell"); |
176 | return 0; | 187 | return 0; |
177 | } | 188 | } |
178 | 189 | ||
@@ -212,7 +223,7 @@ static int afs_parse_options(struct afs_mount_params *params, | |||
212 | break; | 223 | break; |
213 | 224 | ||
214 | case afs_opt_rwpath: | 225 | case afs_opt_rwpath: |
215 | params->rwpath = 1; | 226 | params->rwpath = true; |
216 | break; | 227 | break; |
217 | 228 | ||
218 | case afs_opt_vol: | 229 | case afs_opt_vol: |
@@ -220,7 +231,11 @@ static int afs_parse_options(struct afs_mount_params *params, | |||
220 | break; | 231 | break; |
221 | 232 | ||
222 | case afs_opt_autocell: | 233 | case afs_opt_autocell: |
223 | params->autocell = 1; | 234 | params->autocell = true; |
235 | break; | ||
236 | |||
237 | case afs_opt_dyn: | ||
238 | params->dyn_root = true; | ||
224 | break; | 239 | break; |
225 | 240 | ||
226 | default: | 241 | default: |
@@ -254,7 +269,7 @@ static int afs_parse_device_name(struct afs_mount_params *params, | |||
254 | int cellnamesz; | 269 | int cellnamesz; |
255 | 270 | ||
256 | _enter(",%s", name); | 271 | _enter(",%s", name); |
257 | 272 | ||
258 | if (!name) { | 273 | if (!name) { |
259 | printk(KERN_ERR "kAFS: no volume name specified\n"); | 274 | printk(KERN_ERR "kAFS: no volume name specified\n"); |
260 | return -EINVAL; | 275 | return -EINVAL; |
@@ -336,7 +351,14 @@ static int afs_test_super(struct super_block *sb, void *data) | |||
336 | struct afs_super_info *as1 = data; | 351 | struct afs_super_info *as1 = data; |
337 | struct afs_super_info *as = AFS_FS_S(sb); | 352 | struct afs_super_info *as = AFS_FS_S(sb); |
338 | 353 | ||
339 | return as->net == as1->net && as->volume->vid == as1->volume->vid; | 354 | return (as->net == as1->net && |
355 | as->volume && | ||
356 | as->volume->vid == as1->volume->vid); | ||
357 | } | ||
358 | |||
359 | static int afs_dynroot_test_super(struct super_block *sb, void *data) | ||
360 | { | ||
361 | return false; | ||
340 | } | 362 | } |
341 | 363 | ||
342 | static int afs_set_super(struct super_block *sb, void *data) | 364 | static int afs_set_super(struct super_block *sb, void *data) |
@@ -365,24 +387,30 @@ static int afs_fill_super(struct super_block *sb, | |||
365 | sb->s_blocksize_bits = PAGE_SHIFT; | 387 | sb->s_blocksize_bits = PAGE_SHIFT; |
366 | sb->s_magic = AFS_FS_MAGIC; | 388 | sb->s_magic = AFS_FS_MAGIC; |
367 | sb->s_op = &afs_super_ops; | 389 | sb->s_op = &afs_super_ops; |
368 | sb->s_xattr = afs_xattr_handlers; | 390 | if (!as->dyn_root) |
391 | sb->s_xattr = afs_xattr_handlers; | ||
369 | ret = super_setup_bdi(sb); | 392 | ret = super_setup_bdi(sb); |
370 | if (ret) | 393 | if (ret) |
371 | return ret; | 394 | return ret; |
372 | sb->s_bdi->ra_pages = VM_MAX_READAHEAD * 1024 / PAGE_SIZE; | 395 | sb->s_bdi->ra_pages = VM_MAX_READAHEAD * 1024 / PAGE_SIZE; |
373 | sprintf(sb->s_id, "%u", as->volume->vid); | ||
374 | |||
375 | afs_activate_volume(as->volume); | ||
376 | 396 | ||
377 | /* allocate the root inode and dentry */ | 397 | /* allocate the root inode and dentry */ |
378 | fid.vid = as->volume->vid; | 398 | if (as->dyn_root) { |
379 | fid.vnode = 1; | 399 | inode = afs_iget_pseudo_dir(sb, true); |
380 | fid.unique = 1; | 400 | sb->s_flags |= SB_RDONLY; |
381 | inode = afs_iget(sb, params->key, &fid, NULL, NULL, NULL); | 401 | } else { |
402 | sprintf(sb->s_id, "%u", as->volume->vid); | ||
403 | afs_activate_volume(as->volume); | ||
404 | fid.vid = as->volume->vid; | ||
405 | fid.vnode = 1; | ||
406 | fid.unique = 1; | ||
407 | inode = afs_iget(sb, params->key, &fid, NULL, NULL, NULL); | ||
408 | } | ||
409 | |||
382 | if (IS_ERR(inode)) | 410 | if (IS_ERR(inode)) |
383 | return PTR_ERR(inode); | 411 | return PTR_ERR(inode); |
384 | 412 | ||
385 | if (params->autocell) | 413 | if (params->autocell || params->dyn_root) |
386 | set_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(inode)->flags); | 414 | set_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(inode)->flags); |
387 | 415 | ||
388 | ret = -ENOMEM; | 416 | ret = -ENOMEM; |
@@ -407,7 +435,10 @@ static struct afs_super_info *afs_alloc_sbi(struct afs_mount_params *params) | |||
407 | as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL); | 435 | as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL); |
408 | if (as) { | 436 | if (as) { |
409 | as->net = afs_get_net(params->net); | 437 | as->net = afs_get_net(params->net); |
410 | as->cell = afs_get_cell(params->cell); | 438 | if (params->dyn_root) |
439 | as->dyn_root = true; | ||
440 | else | ||
441 | as->cell = afs_get_cell(params->cell); | ||
411 | } | 442 | } |
412 | return as; | 443 | return as; |
413 | } | 444 | } |
@@ -451,18 +482,20 @@ static struct dentry *afs_mount(struct file_system_type *fs_type, | |||
451 | goto error; | 482 | goto error; |
452 | } | 483 | } |
453 | 484 | ||
454 | ret = afs_parse_device_name(¶ms, dev_name); | 485 | if (!params.dyn_root) { |
455 | if (ret < 0) | 486 | ret = afs_parse_device_name(¶ms, dev_name); |
456 | goto error; | 487 | if (ret < 0) |
488 | goto error; | ||
457 | 489 | ||
458 | /* try and do the mount securely */ | 490 | /* try and do the mount securely */ |
459 | key = afs_request_key(params.cell); | 491 | key = afs_request_key(params.cell); |
460 | if (IS_ERR(key)) { | 492 | if (IS_ERR(key)) { |
461 | _leave(" = %ld [key]", PTR_ERR(key)); | 493 | _leave(" = %ld [key]", PTR_ERR(key)); |
462 | ret = PTR_ERR(key); | 494 | ret = PTR_ERR(key); |
463 | goto error; | 495 | goto error; |
496 | } | ||
497 | params.key = key; | ||
464 | } | 498 | } |
465 | params.key = key; | ||
466 | 499 | ||
467 | /* allocate a superblock info record */ | 500 | /* allocate a superblock info record */ |
468 | ret = -ENOMEM; | 501 | ret = -ENOMEM; |
@@ -470,20 +503,25 @@ static struct dentry *afs_mount(struct file_system_type *fs_type, | |||
470 | if (!as) | 503 | if (!as) |
471 | goto error_key; | 504 | goto error_key; |
472 | 505 | ||
473 | /* Assume we're going to need a volume record; at the very least we can | 506 | if (!params.dyn_root) { |
474 | * use it to update the volume record if we have one already. This | 507 | /* Assume we're going to need a volume record; at the very |
475 | * checks that the volume exists within the cell. | 508 | * least we can use it to update the volume record if we have |
476 | */ | 509 | * one already. This checks that the volume exists within the |
477 | candidate = afs_create_volume(¶ms); | 510 | * cell. |
478 | if (IS_ERR(candidate)) { | 511 | */ |
479 | ret = PTR_ERR(candidate); | 512 | candidate = afs_create_volume(¶ms); |
480 | goto error_as; | 513 | if (IS_ERR(candidate)) { |
481 | } | 514 | ret = PTR_ERR(candidate); |
515 | goto error_as; | ||
516 | } | ||
482 | 517 | ||
483 | as->volume = candidate; | 518 | as->volume = candidate; |
519 | } | ||
484 | 520 | ||
485 | /* allocate a deviceless superblock */ | 521 | /* allocate a deviceless superblock */ |
486 | sb = sget(fs_type, afs_test_super, afs_set_super, flags, as); | 522 | sb = sget(fs_type, |
523 | as->dyn_root ? afs_dynroot_test_super : afs_test_super, | ||
524 | afs_set_super, flags, as); | ||
487 | if (IS_ERR(sb)) { | 525 | if (IS_ERR(sb)) { |
488 | ret = PTR_ERR(sb); | 526 | ret = PTR_ERR(sb); |
489 | goto error_as; | 527 | goto error_as; |
@@ -529,9 +567,11 @@ static void afs_kill_super(struct super_block *sb) | |||
529 | /* Clear the callback interests (which will do ilookup5) before | 567 | /* Clear the callback interests (which will do ilookup5) before |
530 | * deactivating the superblock. | 568 | * deactivating the superblock. |
531 | */ | 569 | */ |
532 | afs_clear_callback_interests(as->net, as->volume->servers); | 570 | if (as->volume) |
571 | afs_clear_callback_interests(as->net, as->volume->servers); | ||
533 | kill_anon_super(sb); | 572 | kill_anon_super(sb); |
534 | afs_deactivate_volume(as->volume); | 573 | if (as->volume) |
574 | afs_deactivate_volume(as->volume); | ||
535 | afs_destroy_sbi(as); | 575 | afs_destroy_sbi(as); |
536 | } | 576 | } |
537 | 577 | ||
@@ -619,12 +659,24 @@ static void afs_destroy_inode(struct inode *inode) | |||
619 | */ | 659 | */ |
620 | static int afs_statfs(struct dentry *dentry, struct kstatfs *buf) | 660 | static int afs_statfs(struct dentry *dentry, struct kstatfs *buf) |
621 | { | 661 | { |
662 | struct afs_super_info *as = AFS_FS_S(dentry->d_sb); | ||
622 | struct afs_fs_cursor fc; | 663 | struct afs_fs_cursor fc; |
623 | struct afs_volume_status vs; | 664 | struct afs_volume_status vs; |
624 | struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry)); | 665 | struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry)); |
625 | struct key *key; | 666 | struct key *key; |
626 | int ret; | 667 | int ret; |
627 | 668 | ||
669 | buf->f_type = dentry->d_sb->s_magic; | ||
670 | buf->f_bsize = AFS_BLOCK_SIZE; | ||
671 | buf->f_namelen = AFSNAMEMAX - 1; | ||
672 | |||
673 | if (as->dyn_root) { | ||
674 | buf->f_blocks = 1; | ||
675 | buf->f_bavail = 0; | ||
676 | buf->f_bfree = 0; | ||
677 | return 0; | ||
678 | } | ||
679 | |||
628 | key = afs_request_key(vnode->volume->cell); | 680 | key = afs_request_key(vnode->volume->cell); |
629 | if (IS_ERR(key)) | 681 | if (IS_ERR(key)) |
630 | return PTR_ERR(key); | 682 | return PTR_ERR(key); |
@@ -645,10 +697,6 @@ static int afs_statfs(struct dentry *dentry, struct kstatfs *buf) | |||
645 | key_put(key); | 697 | key_put(key); |
646 | 698 | ||
647 | if (ret == 0) { | 699 | if (ret == 0) { |
648 | buf->f_type = dentry->d_sb->s_magic; | ||
649 | buf->f_bsize = AFS_BLOCK_SIZE; | ||
650 | buf->f_namelen = AFSNAMEMAX - 1; | ||
651 | |||
652 | if (vs.max_quota == 0) | 700 | if (vs.max_quota == 0) |
653 | buf->f_blocks = vs.part_max_blocks; | 701 | buf->f_blocks = vs.part_max_blocks; |
654 | else | 702 | else |
diff --git a/net/dns_resolver/dns_query.c b/net/dns_resolver/dns_query.c index af781010753b..49da67034f29 100644 --- a/net/dns_resolver/dns_query.c +++ b/net/dns_resolver/dns_query.c | |||
@@ -52,11 +52,11 @@ | |||
52 | * @name: Name to look up | 52 | * @name: Name to look up |
53 | * @namelen: Length of name | 53 | * @namelen: Length of name |
54 | * @options: Request options (or NULL if no options) | 54 | * @options: Request options (or NULL if no options) |
55 | * @_result: Where to place the returned data. | 55 | * @_result: Where to place the returned data (or NULL) |
56 | * @_expiry: Where to store the result expiry time (or NULL) | 56 | * @_expiry: Where to store the result expiry time (or NULL) |
57 | * | 57 | * |
58 | * The data will be returned in the pointer at *result, and the caller is | 58 | * The data will be returned in the pointer at *result, if provided, and the |
59 | * responsible for freeing it. | 59 | * caller is responsible for freeing it. |
60 | * | 60 | * |
61 | * The description should be of the form "[<query_type>:]<domain_name>", and | 61 | * The description should be of the form "[<query_type>:]<domain_name>", and |
62 | * the options need to be appropriate for the query type requested. If no | 62 | * the options need to be appropriate for the query type requested. If no |
@@ -81,7 +81,7 @@ int dns_query(const char *type, const char *name, size_t namelen, | |||
81 | kenter("%s,%*.*s,%zu,%s", | 81 | kenter("%s,%*.*s,%zu,%s", |
82 | type, (int)namelen, (int)namelen, name, namelen, options); | 82 | type, (int)namelen, (int)namelen, name, namelen, options); |
83 | 83 | ||
84 | if (!name || namelen == 0 || !_result) | 84 | if (!name || namelen == 0) |
85 | return -EINVAL; | 85 | return -EINVAL; |
86 | 86 | ||
87 | /* construct the query key description as "[<type>:]<name>" */ | 87 | /* construct the query key description as "[<type>:]<name>" */ |
@@ -146,13 +146,15 @@ int dns_query(const char *type, const char *name, size_t namelen, | |||
146 | upayload = user_key_payload_locked(rkey); | 146 | upayload = user_key_payload_locked(rkey); |
147 | len = upayload->datalen; | 147 | len = upayload->datalen; |
148 | 148 | ||
149 | ret = -ENOMEM; | 149 | if (_result) { |
150 | *_result = kmalloc(len + 1, GFP_KERNEL); | 150 | ret = -ENOMEM; |
151 | if (!*_result) | 151 | *_result = kmalloc(len + 1, GFP_KERNEL); |
152 | goto put; | 152 | if (!*_result) |
153 | goto put; | ||
153 | 154 | ||
154 | memcpy(*_result, upayload->data, len); | 155 | memcpy(*_result, upayload->data, len); |
155 | (*_result)[len] = '\0'; | 156 | (*_result)[len] = '\0'; |
157 | } | ||
156 | 158 | ||
157 | if (_expiry) | 159 | if (_expiry) |
158 | *_expiry = rkey->expiry; | 160 | *_expiry = rkey->expiry; |