summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2018-04-09 16:12:31 -0400
committerDavid Howells <dhowells@redhat.com>2018-04-09 16:12:31 -0400
commit6f8880d8e68155726a2a22e8787cfebf1ffcab08 (patch)
treeb0e11598c62e9d9c01f3a41cadb7f71b1b901692 /fs
parent5cf9dd55a0ec26428f2824aadd16bfa305a5b603 (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.c63
-rw-r--r--fs/afs/internal.h16
-rw-r--r--fs/afs/main.c44
-rw-r--r--fs/afs/proc.c231
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 */
772static 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;
817out_s:
818 afs_put_sysnames(subs);
819 kfree(buf);
820out_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 */
772static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, 828static 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)
199extern struct file_system_type afs_fs_type; 199extern struct file_system_type afs_fs_type;
200 200
201/* 201/*
202 * Set of substitutes for @sys.
203 */
204struct 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 */
204struct afs_net { 216struct 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
265extern const char afs_init_sysname[];
251extern struct afs_net __afs_net;// Dummy AFS network namespace; TODO: replace with real netns 266extern struct afs_net __afs_net;// Dummy AFS network namespace; TODO: replace with real netns
252 267
253enum afs_cell_state { 268enum afs_cell_state {
@@ -789,6 +804,7 @@ extern int __net_init afs_proc_init(struct afs_net *);
789extern void __net_exit afs_proc_cleanup(struct afs_net *); 804extern void __net_exit afs_proc_cleanup(struct afs_net *);
790extern int afs_proc_cell_setup(struct afs_net *, struct afs_cell *); 805extern int afs_proc_cell_setup(struct afs_net *, struct afs_cell *);
791extern void afs_proc_cell_remove(struct afs_net *, struct afs_cell *); 806extern void afs_proc_cell_remove(struct afs_net *, struct afs_cell *);
807extern 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");
34struct workqueue_struct *afs_wq; 34struct workqueue_struct *afs_wq;
35struct afs_net __afs_net; 35struct afs_net __afs_net;
36 36
37#if defined(CONFIG_ALPHA)
38const char afs_init_sysname[] = "alpha_linux26";
39#elif defined(CONFIG_X86_64)
40const char afs_init_sysname[] = "amd64_linux26";
41#elif defined(CONFIG_ARM)
42const char afs_init_sysname[] = "arm_linux26";
43#elif defined(CONFIG_ARM64)
44const char afs_init_sysname[] = "aarch64_linux26";
45#elif defined(CONFIG_X86_32)
46const char afs_init_sysname[] = "i386_linux26";
47#elif defined(CONFIG_IA64)
48const char afs_init_sysname[] = "ia64_linux26";
49#elif defined(CONFIG_PPC64)
50const char afs_init_sysname[] = "ppc64_linux26";
51#elif defined(CONFIG_PPC32)
52const char afs_init_sysname[] = "ppc_linux26";
53#elif defined(CONFIG_S390)
54#ifdef CONFIG_64BIT
55const char afs_init_sysname[] = "s390x_linux26";
56#else
57const char afs_init_sysname[] = "s390_linux26";
58#endif
59#elif defined(CONFIG_SPARC64)
60const char afs_init_sysname[] = "sparc64_linux26";
61#elif defined(CONFIG_SPARC32)
62const char afs_init_sysname[] = "sparc_linux26";
63#else
64const 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 */
40static int __net_init afs_net_init(struct afs_net *net) 70static 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);
94error_proc: 135error_proc:
136 afs_put_sysnames(net->sysnames);
137error_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
129static int afs_proc_sysname_open(struct inode *inode, struct file *file);
130static int afs_proc_sysname_release(struct inode *inode, struct file *file);
131static void *afs_proc_sysname_start(struct seq_file *p, loff_t *pos);
132static void *afs_proc_sysname_next(struct seq_file *p, void *v,
133 loff_t *pos);
134static void afs_proc_sysname_stop(struct seq_file *p, void *v);
135static int afs_proc_sysname_show(struct seq_file *m, void *v);
136static ssize_t afs_proc_sysname_write(struct file *file,
137 const char __user *buf,
138 size_t size, loff_t *_pos);
139
140static 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
147static 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
378out:
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
673void 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 */
690static 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 */
718static 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 */
788out:
789 inode_unlock(file_inode(file));
790 kfree(kbuf);
791 return ret;
792
793invalid:
794 ret = -EINVAL;
795error:
796 sysnames->error = ret;
797 goto out;
798}
799
800static 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
825static 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
838static 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
849static 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
857static 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}