diff options
author | David Howells <dhowells@redhat.com> | 2018-04-09 16:12:31 -0400 |
---|---|---|
committer | David Howells <dhowells@redhat.com> | 2018-04-09 16:12:31 -0400 |
commit | 6f8880d8e68155726a2a22e8787cfebf1ffcab08 (patch) | |
tree | b0e11598c62e9d9c01f3a41cadb7f71b1b901692 /fs | |
parent | 5cf9dd55a0ec26428f2824aadd16bfa305a5b603 (diff) |
afs: Implement @sys substitution handling
Implement the AFS feature by which @sys at the end of a pathname component
may be substituted for one of a list of values, typically naming the
operating system. Up to 16 alternatives may be specified and these are
tried in turn until one works. Each network namespace has[*] a separate
independent list.
Upon creation of a new network namespace, the list of values is
initialised[*] to a single OpenAFS-compatible string representing arch type
plus "_linux26". For example, on x86_64, the sysname is "amd64_linux26".
[*] Or will, once network namespace support is finalised in kAFS.
The list may be set by:
# for i in foo bar linux-x86_64; do echo $i; done >/proc/fs/afs/sysname
for which separate writes to the same fd are amalgamated and applied on
close. The LF character may be used as a separator to specify multiple
items in the same write() call.
The list may be cleared by:
# echo >/proc/fs/afs/sysname
and read by:
# cat /proc/fs/afs/sysname
foo
bar
linux-x86_64
Signed-off-by: David Howells <dhowells@redhat.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/afs/dir.c | 63 | ||||
-rw-r--r-- | fs/afs/internal.h | 16 | ||||
-rw-r--r-- | fs/afs/main.c | 44 | ||||
-rw-r--r-- | fs/afs/proc.c | 231 |
4 files changed, 353 insertions, 1 deletions
diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 27c5231e89e7..3ebd741b74d0 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c | |||
@@ -767,6 +767,62 @@ out: | |||
767 | } | 767 | } |
768 | 768 | ||
769 | /* | 769 | /* |
770 | * Look up an entry in a directory with @sys substitution. | ||
771 | */ | ||
772 | static struct dentry *afs_lookup_atsys(struct inode *dir, struct dentry *dentry, | ||
773 | struct key *key) | ||
774 | { | ||
775 | struct afs_sysnames *subs; | ||
776 | struct afs_net *net = afs_i2net(dir); | ||
777 | struct dentry *ret; | ||
778 | char *buf, *p, *name; | ||
779 | int len, i; | ||
780 | |||
781 | _enter(""); | ||
782 | |||
783 | ret = ERR_PTR(-ENOMEM); | ||
784 | p = buf = kmalloc(AFSNAMEMAX, GFP_KERNEL); | ||
785 | if (!buf) | ||
786 | goto out_p; | ||
787 | if (dentry->d_name.len > 4) { | ||
788 | memcpy(p, dentry->d_name.name, dentry->d_name.len - 4); | ||
789 | p += dentry->d_name.len - 4; | ||
790 | } | ||
791 | |||
792 | /* There is an ordered list of substitutes that we have to try. */ | ||
793 | read_lock(&net->sysnames_lock); | ||
794 | subs = net->sysnames; | ||
795 | refcount_inc(&subs->usage); | ||
796 | read_unlock(&net->sysnames_lock); | ||
797 | |||
798 | for (i = 0; i < subs->nr; i++) { | ||
799 | name = subs->subs[i]; | ||
800 | len = dentry->d_name.len - 4 + strlen(name); | ||
801 | if (len >= AFSNAMEMAX) { | ||
802 | ret = ERR_PTR(-ENAMETOOLONG); | ||
803 | goto out_s; | ||
804 | } | ||
805 | |||
806 | strcpy(p, name); | ||
807 | ret = lookup_one_len(buf, dentry->d_parent, len); | ||
808 | if (IS_ERR(ret) || d_is_positive(ret)) | ||
809 | goto out_s; | ||
810 | dput(ret); | ||
811 | } | ||
812 | |||
813 | /* We don't want to d_add() the @sys dentry here as we don't want to | ||
814 | * the cached dentry to hide changes to the sysnames list. | ||
815 | */ | ||
816 | ret = NULL; | ||
817 | out_s: | ||
818 | afs_put_sysnames(subs); | ||
819 | kfree(buf); | ||
820 | out_p: | ||
821 | key_put(key); | ||
822 | return ret; | ||
823 | } | ||
824 | |||
825 | /* | ||
770 | * look up an entry in a directory | 826 | * look up an entry in a directory |
771 | */ | 827 | */ |
772 | static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, | 828 | static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, |
@@ -805,6 +861,13 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, | |||
805 | return ERR_PTR(ret); | 861 | return ERR_PTR(ret); |
806 | } | 862 | } |
807 | 863 | ||
864 | if (dentry->d_name.len >= 4 && | ||
865 | dentry->d_name.name[dentry->d_name.len - 4] == '@' && | ||
866 | dentry->d_name.name[dentry->d_name.len - 3] == 's' && | ||
867 | dentry->d_name.name[dentry->d_name.len - 2] == 'y' && | ||
868 | dentry->d_name.name[dentry->d_name.len - 1] == 's') | ||
869 | return afs_lookup_atsys(dir, dentry, key); | ||
870 | |||
808 | inode = afs_do_lookup(dir, dentry, key); | 871 | inode = afs_do_lookup(dir, dentry, key); |
809 | if (IS_ERR(inode)) { | 872 | if (IS_ERR(inode)) { |
810 | ret = PTR_ERR(inode); | 873 | ret = PTR_ERR(inode); |
diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 55b07e818400..27150bbc50d8 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h | |||
@@ -199,6 +199,18 @@ static inline struct afs_super_info *AFS_FS_S(struct super_block *sb) | |||
199 | extern struct file_system_type afs_fs_type; | 199 | extern struct file_system_type afs_fs_type; |
200 | 200 | ||
201 | /* | 201 | /* |
202 | * Set of substitutes for @sys. | ||
203 | */ | ||
204 | struct afs_sysnames { | ||
205 | #define AFS_NR_SYSNAME 16 | ||
206 | char *subs[AFS_NR_SYSNAME]; | ||
207 | refcount_t usage; | ||
208 | unsigned short nr; | ||
209 | short error; | ||
210 | char blank[1]; | ||
211 | }; | ||
212 | |||
213 | /* | ||
202 | * AFS network namespace record. | 214 | * AFS network namespace record. |
203 | */ | 215 | */ |
204 | struct afs_net { | 216 | struct afs_net { |
@@ -246,8 +258,11 @@ struct afs_net { | |||
246 | 258 | ||
247 | /* Misc */ | 259 | /* Misc */ |
248 | struct proc_dir_entry *proc_afs; /* /proc/net/afs directory */ | 260 | struct proc_dir_entry *proc_afs; /* /proc/net/afs directory */ |
261 | struct afs_sysnames *sysnames; | ||
262 | rwlock_t sysnames_lock; | ||
249 | }; | 263 | }; |
250 | 264 | ||
265 | extern const char afs_init_sysname[]; | ||
251 | extern struct afs_net __afs_net;// Dummy AFS network namespace; TODO: replace with real netns | 266 | extern struct afs_net __afs_net;// Dummy AFS network namespace; TODO: replace with real netns |
252 | 267 | ||
253 | enum afs_cell_state { | 268 | enum afs_cell_state { |
@@ -789,6 +804,7 @@ extern int __net_init afs_proc_init(struct afs_net *); | |||
789 | extern void __net_exit afs_proc_cleanup(struct afs_net *); | 804 | extern void __net_exit afs_proc_cleanup(struct afs_net *); |
790 | extern int afs_proc_cell_setup(struct afs_net *, struct afs_cell *); | 805 | extern int afs_proc_cell_setup(struct afs_net *, struct afs_cell *); |
791 | extern void afs_proc_cell_remove(struct afs_net *, struct afs_cell *); | 806 | extern void afs_proc_cell_remove(struct afs_net *, struct afs_cell *); |
807 | extern void afs_put_sysnames(struct afs_sysnames *); | ||
792 | 808 | ||
793 | /* | 809 | /* |
794 | * rotate.c | 810 | * rotate.c |
diff --git a/fs/afs/main.c b/fs/afs/main.c index 15a02a05ff40..d7560168b3bf 100644 --- a/fs/afs/main.c +++ b/fs/afs/main.c | |||
@@ -34,11 +34,42 @@ MODULE_PARM_DESC(rootcell, "root AFS cell name and VL server IP addr list"); | |||
34 | struct workqueue_struct *afs_wq; | 34 | struct workqueue_struct *afs_wq; |
35 | struct afs_net __afs_net; | 35 | struct afs_net __afs_net; |
36 | 36 | ||
37 | #if defined(CONFIG_ALPHA) | ||
38 | const char afs_init_sysname[] = "alpha_linux26"; | ||
39 | #elif defined(CONFIG_X86_64) | ||
40 | const char afs_init_sysname[] = "amd64_linux26"; | ||
41 | #elif defined(CONFIG_ARM) | ||
42 | const char afs_init_sysname[] = "arm_linux26"; | ||
43 | #elif defined(CONFIG_ARM64) | ||
44 | const char afs_init_sysname[] = "aarch64_linux26"; | ||
45 | #elif defined(CONFIG_X86_32) | ||
46 | const char afs_init_sysname[] = "i386_linux26"; | ||
47 | #elif defined(CONFIG_IA64) | ||
48 | const char afs_init_sysname[] = "ia64_linux26"; | ||
49 | #elif defined(CONFIG_PPC64) | ||
50 | const char afs_init_sysname[] = "ppc64_linux26"; | ||
51 | #elif defined(CONFIG_PPC32) | ||
52 | const char afs_init_sysname[] = "ppc_linux26"; | ||
53 | #elif defined(CONFIG_S390) | ||
54 | #ifdef CONFIG_64BIT | ||
55 | const char afs_init_sysname[] = "s390x_linux26"; | ||
56 | #else | ||
57 | const char afs_init_sysname[] = "s390_linux26"; | ||
58 | #endif | ||
59 | #elif defined(CONFIG_SPARC64) | ||
60 | const char afs_init_sysname[] = "sparc64_linux26"; | ||
61 | #elif defined(CONFIG_SPARC32) | ||
62 | const char afs_init_sysname[] = "sparc_linux26"; | ||
63 | #else | ||
64 | const char afs_init_sysname[] = "unknown_linux26"; | ||
65 | #endif | ||
66 | |||
37 | /* | 67 | /* |
38 | * Initialise an AFS network namespace record. | 68 | * Initialise an AFS network namespace record. |
39 | */ | 69 | */ |
40 | static int __net_init afs_net_init(struct afs_net *net) | 70 | static int __net_init afs_net_init(struct afs_net *net) |
41 | { | 71 | { |
72 | struct afs_sysnames *sysnames; | ||
42 | int ret; | 73 | int ret; |
43 | 74 | ||
44 | net->live = true; | 75 | net->live = true; |
@@ -67,6 +98,16 @@ static int __net_init afs_net_init(struct afs_net *net) | |||
67 | INIT_WORK(&net->fs_manager, afs_manage_servers); | 98 | INIT_WORK(&net->fs_manager, afs_manage_servers); |
68 | timer_setup(&net->fs_timer, afs_servers_timer, 0); | 99 | timer_setup(&net->fs_timer, afs_servers_timer, 0); |
69 | 100 | ||
101 | ret = -ENOMEM; | ||
102 | sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL); | ||
103 | if (!sysnames) | ||
104 | goto error_sysnames; | ||
105 | sysnames->subs[0] = (char *)&afs_init_sysname; | ||
106 | sysnames->nr = 1; | ||
107 | refcount_set(&sysnames->usage, 1); | ||
108 | net->sysnames = sysnames; | ||
109 | rwlock_init(&net->sysnames_lock); | ||
110 | |||
70 | /* Register the /proc stuff */ | 111 | /* Register the /proc stuff */ |
71 | ret = afs_proc_init(net); | 112 | ret = afs_proc_init(net); |
72 | if (ret < 0) | 113 | if (ret < 0) |
@@ -92,6 +133,8 @@ error_cell_init: | |||
92 | net->live = false; | 133 | net->live = false; |
93 | afs_proc_cleanup(net); | 134 | afs_proc_cleanup(net); |
94 | error_proc: | 135 | error_proc: |
136 | afs_put_sysnames(net->sysnames); | ||
137 | error_sysnames: | ||
95 | net->live = false; | 138 | net->live = false; |
96 | return ret; | 139 | return ret; |
97 | } | 140 | } |
@@ -106,6 +149,7 @@ static void __net_exit afs_net_exit(struct afs_net *net) | |||
106 | afs_purge_servers(net); | 149 | afs_purge_servers(net); |
107 | afs_close_socket(net); | 150 | afs_close_socket(net); |
108 | afs_proc_cleanup(net); | 151 | afs_proc_cleanup(net); |
152 | afs_put_sysnames(net->sysnames); | ||
109 | } | 153 | } |
110 | 154 | ||
111 | /* | 155 | /* |
diff --git a/fs/afs/proc.c b/fs/afs/proc.c index 2f04d37eeef0..15650cd59404 100644 --- a/fs/afs/proc.c +++ b/fs/afs/proc.c | |||
@@ -126,6 +126,32 @@ static const struct file_operations afs_proc_servers_fops = { | |||
126 | .release = seq_release, | 126 | .release = seq_release, |
127 | }; | 127 | }; |
128 | 128 | ||
129 | static int afs_proc_sysname_open(struct inode *inode, struct file *file); | ||
130 | static int afs_proc_sysname_release(struct inode *inode, struct file *file); | ||
131 | static void *afs_proc_sysname_start(struct seq_file *p, loff_t *pos); | ||
132 | static void *afs_proc_sysname_next(struct seq_file *p, void *v, | ||
133 | loff_t *pos); | ||
134 | static void afs_proc_sysname_stop(struct seq_file *p, void *v); | ||
135 | static int afs_proc_sysname_show(struct seq_file *m, void *v); | ||
136 | static ssize_t afs_proc_sysname_write(struct file *file, | ||
137 | const char __user *buf, | ||
138 | size_t size, loff_t *_pos); | ||
139 | |||
140 | static const struct seq_operations afs_proc_sysname_ops = { | ||
141 | .start = afs_proc_sysname_start, | ||
142 | .next = afs_proc_sysname_next, | ||
143 | .stop = afs_proc_sysname_stop, | ||
144 | .show = afs_proc_sysname_show, | ||
145 | }; | ||
146 | |||
147 | static const struct file_operations afs_proc_sysname_fops = { | ||
148 | .open = afs_proc_sysname_open, | ||
149 | .read = seq_read, | ||
150 | .llseek = seq_lseek, | ||
151 | .release = afs_proc_sysname_release, | ||
152 | .write = afs_proc_sysname_write, | ||
153 | }; | ||
154 | |||
129 | /* | 155 | /* |
130 | * initialise the /proc/fs/afs/ directory | 156 | * initialise the /proc/fs/afs/ directory |
131 | */ | 157 | */ |
@@ -139,7 +165,8 @@ int afs_proc_init(struct afs_net *net) | |||
139 | 165 | ||
140 | if (!proc_create("cells", 0644, net->proc_afs, &afs_proc_cells_fops) || | 166 | if (!proc_create("cells", 0644, net->proc_afs, &afs_proc_cells_fops) || |
141 | !proc_create("rootcell", 0644, net->proc_afs, &afs_proc_rootcell_fops) || | 167 | !proc_create("rootcell", 0644, net->proc_afs, &afs_proc_rootcell_fops) || |
142 | !proc_create("servers", 0644, net->proc_afs, &afs_proc_servers_fops)) | 168 | !proc_create("servers", 0644, net->proc_afs, &afs_proc_servers_fops) || |
169 | !proc_create("sysname", 0644, net->proc_afs, &afs_proc_sysname_fops)) | ||
143 | goto error_tree; | 170 | goto error_tree; |
144 | 171 | ||
145 | _leave(" = 0"); | 172 | _leave(" = 0"); |
@@ -330,6 +357,12 @@ static ssize_t afs_proc_rootcell_write(struct file *file, | |||
330 | if (IS_ERR(kbuf)) | 357 | if (IS_ERR(kbuf)) |
331 | return PTR_ERR(kbuf); | 358 | return PTR_ERR(kbuf); |
332 | 359 | ||
360 | ret = -EINVAL; | ||
361 | if (kbuf[0] == '.') | ||
362 | goto out; | ||
363 | if (memchr(kbuf, '/', size)) | ||
364 | goto out; | ||
365 | |||
333 | /* trim to first NL */ | 366 | /* trim to first NL */ |
334 | s = memchr(kbuf, '\n', size); | 367 | s = memchr(kbuf, '\n', size); |
335 | if (s) | 368 | if (s) |
@@ -342,6 +375,7 @@ static ssize_t afs_proc_rootcell_write(struct file *file, | |||
342 | if (ret >= 0) | 375 | if (ret >= 0) |
343 | ret = size; /* consume everything, always */ | 376 | ret = size; /* consume everything, always */ |
344 | 377 | ||
378 | out: | ||
345 | kfree(kbuf); | 379 | kfree(kbuf); |
346 | _leave(" = %d", ret); | 380 | _leave(" = %d", ret); |
347 | return ret; | 381 | return ret; |
@@ -635,3 +669,198 @@ static int afs_proc_servers_show(struct seq_file *m, void *v) | |||
635 | &alist->addrs[alist->index].transport); | 669 | &alist->addrs[alist->index].transport); |
636 | return 0; | 670 | return 0; |
637 | } | 671 | } |
672 | |||
673 | void afs_put_sysnames(struct afs_sysnames *sysnames) | ||
674 | { | ||
675 | int i; | ||
676 | |||
677 | if (sysnames && refcount_dec_and_test(&sysnames->usage)) { | ||
678 | for (i = 0; i < sysnames->nr; i++) | ||
679 | if (sysnames->subs[i] != afs_init_sysname && | ||
680 | sysnames->subs[i] != sysnames->blank) | ||
681 | kfree(sysnames->subs[i]); | ||
682 | } | ||
683 | } | ||
684 | |||
685 | /* | ||
686 | * Handle opening of /proc/fs/afs/sysname. If it is opened for writing, we | ||
687 | * assume the caller wants to change the substitution list and we allocate a | ||
688 | * buffer to hold the list. | ||
689 | */ | ||
690 | static int afs_proc_sysname_open(struct inode *inode, struct file *file) | ||
691 | { | ||
692 | struct afs_sysnames *sysnames; | ||
693 | struct seq_file *m; | ||
694 | int ret; | ||
695 | |||
696 | ret = seq_open(file, &afs_proc_sysname_ops); | ||
697 | if (ret < 0) | ||
698 | return ret; | ||
699 | |||
700 | if (file->f_mode & FMODE_WRITE) { | ||
701 | sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL); | ||
702 | if (!sysnames) { | ||
703 | seq_release(inode, file); | ||
704 | return -ENOMEM; | ||
705 | } | ||
706 | |||
707 | refcount_set(&sysnames->usage, 1); | ||
708 | m = file->private_data; | ||
709 | m->private = sysnames; | ||
710 | } | ||
711 | |||
712 | return 0; | ||
713 | } | ||
714 | |||
715 | /* | ||
716 | * Handle writes to /proc/fs/afs/sysname to set the @sys substitution. | ||
717 | */ | ||
718 | static ssize_t afs_proc_sysname_write(struct file *file, | ||
719 | const char __user *buf, | ||
720 | size_t size, loff_t *_pos) | ||
721 | { | ||
722 | struct afs_sysnames *sysnames; | ||
723 | struct seq_file *m = file->private_data; | ||
724 | char *kbuf = NULL, *s, *p, *sub; | ||
725 | int ret, len; | ||
726 | |||
727 | sysnames = m->private; | ||
728 | if (!sysnames) | ||
729 | return -EINVAL; | ||
730 | if (sysnames->error) | ||
731 | return sysnames->error; | ||
732 | |||
733 | if (size >= PAGE_SIZE - 1) { | ||
734 | sysnames->error = -EINVAL; | ||
735 | return -EINVAL; | ||
736 | } | ||
737 | if (size == 0) | ||
738 | return 0; | ||
739 | |||
740 | kbuf = memdup_user_nul(buf, size); | ||
741 | if (IS_ERR(kbuf)) | ||
742 | return PTR_ERR(kbuf); | ||
743 | |||
744 | inode_lock(file_inode(file)); | ||
745 | |||
746 | p = kbuf; | ||
747 | while ((s = strsep(&p, " \t\n"))) { | ||
748 | len = strlen(s); | ||
749 | if (len == 0) | ||
750 | continue; | ||
751 | ret = -ENAMETOOLONG; | ||
752 | if (len >= AFSNAMEMAX) | ||
753 | goto error; | ||
754 | |||
755 | if (len >= 4 && | ||
756 | s[len - 4] == '@' && | ||
757 | s[len - 3] == 's' && | ||
758 | s[len - 2] == 'y' && | ||
759 | s[len - 1] == 's') | ||
760 | /* Protect against recursion */ | ||
761 | goto invalid; | ||
762 | |||
763 | if (s[0] == '.' && | ||
764 | (len < 2 || (len == 2 && s[1] == '.'))) | ||
765 | goto invalid; | ||
766 | |||
767 | if (memchr(s, '/', len)) | ||
768 | goto invalid; | ||
769 | |||
770 | ret = -EFBIG; | ||
771 | if (sysnames->nr >= AFS_NR_SYSNAME) | ||
772 | goto out; | ||
773 | |||
774 | if (strcmp(s, afs_init_sysname) == 0) { | ||
775 | sub = (char *)afs_init_sysname; | ||
776 | } else { | ||
777 | ret = -ENOMEM; | ||
778 | sub = kmemdup(s, len + 1, GFP_KERNEL); | ||
779 | if (!sub) | ||
780 | goto out; | ||
781 | } | ||
782 | |||
783 | sysnames->subs[sysnames->nr] = sub; | ||
784 | sysnames->nr++; | ||
785 | } | ||
786 | |||
787 | ret = size; /* consume everything, always */ | ||
788 | out: | ||
789 | inode_unlock(file_inode(file)); | ||
790 | kfree(kbuf); | ||
791 | return ret; | ||
792 | |||
793 | invalid: | ||
794 | ret = -EINVAL; | ||
795 | error: | ||
796 | sysnames->error = ret; | ||
797 | goto out; | ||
798 | } | ||
799 | |||
800 | static int afs_proc_sysname_release(struct inode *inode, struct file *file) | ||
801 | { | ||
802 | struct afs_sysnames *sysnames, *kill = NULL; | ||
803 | struct seq_file *m = file->private_data; | ||
804 | struct afs_net *net = afs_seq2net(m); | ||
805 | |||
806 | sysnames = m->private; | ||
807 | if (sysnames) { | ||
808 | if (!sysnames->error) { | ||
809 | kill = sysnames; | ||
810 | if (sysnames->nr == 0) { | ||
811 | sysnames->subs[0] = sysnames->blank; | ||
812 | sysnames->nr++; | ||
813 | } | ||
814 | write_lock(&net->sysnames_lock); | ||
815 | kill = net->sysnames; | ||
816 | net->sysnames = sysnames; | ||
817 | write_unlock(&net->sysnames_lock); | ||
818 | } | ||
819 | afs_put_sysnames(kill); | ||
820 | } | ||
821 | |||
822 | return seq_release(inode, file); | ||
823 | } | ||
824 | |||
825 | static void *afs_proc_sysname_start(struct seq_file *m, loff_t *pos) | ||
826 | __acquires(&net->sysnames_lock) | ||
827 | { | ||
828 | struct afs_net *net = afs_seq2net(m); | ||
829 | struct afs_sysnames *names = net->sysnames; | ||
830 | |||
831 | read_lock(&net->sysnames_lock); | ||
832 | |||
833 | if (*pos >= names->nr) | ||
834 | return NULL; | ||
835 | return (void *)(unsigned long)(*pos + 1); | ||
836 | } | ||
837 | |||
838 | static void *afs_proc_sysname_next(struct seq_file *m, void *v, loff_t *pos) | ||
839 | { | ||
840 | struct afs_net *net = afs_seq2net(m); | ||
841 | struct afs_sysnames *names = net->sysnames; | ||
842 | |||
843 | *pos += 1; | ||
844 | if (*pos >= names->nr) | ||
845 | return NULL; | ||
846 | return (void *)(unsigned long)(*pos + 1); | ||
847 | } | ||
848 | |||
849 | static void afs_proc_sysname_stop(struct seq_file *m, void *v) | ||
850 | __releases(&net->sysnames_lock) | ||
851 | { | ||
852 | struct afs_net *net = afs_seq2net(m); | ||
853 | |||
854 | read_unlock(&net->sysnames_lock); | ||
855 | } | ||
856 | |||
857 | static int afs_proc_sysname_show(struct seq_file *m, void *v) | ||
858 | { | ||
859 | struct afs_net *net = afs_seq2net(m); | ||
860 | struct afs_sysnames *sysnames = net->sysnames; | ||
861 | unsigned int i = (unsigned long)v - 1; | ||
862 | |||
863 | if (i < sysnames->nr) | ||
864 | seq_printf(m, "%s\n", sysnames->subs[i]); | ||
865 | return 0; | ||
866 | } | ||