|
|
@@ -126,6 +126,32 @@ static const struct file_operations afs_proc_servers_fops = {
|
|
|
.release = seq_release,
|
|
|
};
|
|
|
|
|
|
+static int afs_proc_sysname_open(struct inode *inode, struct file *file);
|
|
|
+static int afs_proc_sysname_release(struct inode *inode, struct file *file);
|
|
|
+static void *afs_proc_sysname_start(struct seq_file *p, loff_t *pos);
|
|
|
+static void *afs_proc_sysname_next(struct seq_file *p, void *v,
|
|
|
+ loff_t *pos);
|
|
|
+static void afs_proc_sysname_stop(struct seq_file *p, void *v);
|
|
|
+static int afs_proc_sysname_show(struct seq_file *m, void *v);
|
|
|
+static ssize_t afs_proc_sysname_write(struct file *file,
|
|
|
+ const char __user *buf,
|
|
|
+ size_t size, loff_t *_pos);
|
|
|
+
|
|
|
+static const struct seq_operations afs_proc_sysname_ops = {
|
|
|
+ .start = afs_proc_sysname_start,
|
|
|
+ .next = afs_proc_sysname_next,
|
|
|
+ .stop = afs_proc_sysname_stop,
|
|
|
+ .show = afs_proc_sysname_show,
|
|
|
+};
|
|
|
+
|
|
|
+static const struct file_operations afs_proc_sysname_fops = {
|
|
|
+ .open = afs_proc_sysname_open,
|
|
|
+ .read = seq_read,
|
|
|
+ .llseek = seq_lseek,
|
|
|
+ .release = afs_proc_sysname_release,
|
|
|
+ .write = afs_proc_sysname_write,
|
|
|
+};
|
|
|
+
|
|
|
/*
|
|
|
* initialise the /proc/fs/afs/ directory
|
|
|
*/
|
|
|
@@ -139,7 +165,8 @@ int afs_proc_init(struct afs_net *net)
|
|
|
|
|
|
if (!proc_create("cells", 0644, net->proc_afs, &afs_proc_cells_fops) ||
|
|
|
!proc_create("rootcell", 0644, net->proc_afs, &afs_proc_rootcell_fops) ||
|
|
|
- !proc_create("servers", 0644, net->proc_afs, &afs_proc_servers_fops))
|
|
|
+ !proc_create("servers", 0644, net->proc_afs, &afs_proc_servers_fops) ||
|
|
|
+ !proc_create("sysname", 0644, net->proc_afs, &afs_proc_sysname_fops))
|
|
|
goto error_tree;
|
|
|
|
|
|
_leave(" = 0");
|
|
|
@@ -330,6 +357,12 @@ static ssize_t afs_proc_rootcell_write(struct file *file,
|
|
|
if (IS_ERR(kbuf))
|
|
|
return PTR_ERR(kbuf);
|
|
|
|
|
|
+ ret = -EINVAL;
|
|
|
+ if (kbuf[0] == '.')
|
|
|
+ goto out;
|
|
|
+ if (memchr(kbuf, '/', size))
|
|
|
+ goto out;
|
|
|
+
|
|
|
/* trim to first NL */
|
|
|
s = memchr(kbuf, '\n', size);
|
|
|
if (s)
|
|
|
@@ -342,6 +375,7 @@ static ssize_t afs_proc_rootcell_write(struct file *file,
|
|
|
if (ret >= 0)
|
|
|
ret = size; /* consume everything, always */
|
|
|
|
|
|
+out:
|
|
|
kfree(kbuf);
|
|
|
_leave(" = %d", ret);
|
|
|
return ret;
|
|
|
@@ -635,3 +669,198 @@ static int afs_proc_servers_show(struct seq_file *m, void *v)
|
|
|
&alist->addrs[alist->index].transport);
|
|
|
return 0;
|
|
|
}
|
|
|
+
|
|
|
+void afs_put_sysnames(struct afs_sysnames *sysnames)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (sysnames && refcount_dec_and_test(&sysnames->usage)) {
|
|
|
+ for (i = 0; i < sysnames->nr; i++)
|
|
|
+ if (sysnames->subs[i] != afs_init_sysname &&
|
|
|
+ sysnames->subs[i] != sysnames->blank)
|
|
|
+ kfree(sysnames->subs[i]);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Handle opening of /proc/fs/afs/sysname. If it is opened for writing, we
|
|
|
+ * assume the caller wants to change the substitution list and we allocate a
|
|
|
+ * buffer to hold the list.
|
|
|
+ */
|
|
|
+static int afs_proc_sysname_open(struct inode *inode, struct file *file)
|
|
|
+{
|
|
|
+ struct afs_sysnames *sysnames;
|
|
|
+ struct seq_file *m;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = seq_open(file, &afs_proc_sysname_ops);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ if (file->f_mode & FMODE_WRITE) {
|
|
|
+ sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL);
|
|
|
+ if (!sysnames) {
|
|
|
+ seq_release(inode, file);
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ refcount_set(&sysnames->usage, 1);
|
|
|
+ m = file->private_data;
|
|
|
+ m->private = sysnames;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Handle writes to /proc/fs/afs/sysname to set the @sys substitution.
|
|
|
+ */
|
|
|
+static ssize_t afs_proc_sysname_write(struct file *file,
|
|
|
+ const char __user *buf,
|
|
|
+ size_t size, loff_t *_pos)
|
|
|
+{
|
|
|
+ struct afs_sysnames *sysnames;
|
|
|
+ struct seq_file *m = file->private_data;
|
|
|
+ char *kbuf = NULL, *s, *p, *sub;
|
|
|
+ int ret, len;
|
|
|
+
|
|
|
+ sysnames = m->private;
|
|
|
+ if (!sysnames)
|
|
|
+ return -EINVAL;
|
|
|
+ if (sysnames->error)
|
|
|
+ return sysnames->error;
|
|
|
+
|
|
|
+ if (size >= PAGE_SIZE - 1) {
|
|
|
+ sysnames->error = -EINVAL;
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ if (size == 0)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ kbuf = memdup_user_nul(buf, size);
|
|
|
+ if (IS_ERR(kbuf))
|
|
|
+ return PTR_ERR(kbuf);
|
|
|
+
|
|
|
+ inode_lock(file_inode(file));
|
|
|
+
|
|
|
+ p = kbuf;
|
|
|
+ while ((s = strsep(&p, " \t\n"))) {
|
|
|
+ len = strlen(s);
|
|
|
+ if (len == 0)
|
|
|
+ continue;
|
|
|
+ ret = -ENAMETOOLONG;
|
|
|
+ if (len >= AFSNAMEMAX)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ if (len >= 4 &&
|
|
|
+ s[len - 4] == '@' &&
|
|
|
+ s[len - 3] == 's' &&
|
|
|
+ s[len - 2] == 'y' &&
|
|
|
+ s[len - 1] == 's')
|
|
|
+ /* Protect against recursion */
|
|
|
+ goto invalid;
|
|
|
+
|
|
|
+ if (s[0] == '.' &&
|
|
|
+ (len < 2 || (len == 2 && s[1] == '.')))
|
|
|
+ goto invalid;
|
|
|
+
|
|
|
+ if (memchr(s, '/', len))
|
|
|
+ goto invalid;
|
|
|
+
|
|
|
+ ret = -EFBIG;
|
|
|
+ if (sysnames->nr >= AFS_NR_SYSNAME)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ if (strcmp(s, afs_init_sysname) == 0) {
|
|
|
+ sub = (char *)afs_init_sysname;
|
|
|
+ } else {
|
|
|
+ ret = -ENOMEM;
|
|
|
+ sub = kmemdup(s, len + 1, GFP_KERNEL);
|
|
|
+ if (!sub)
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ sysnames->subs[sysnames->nr] = sub;
|
|
|
+ sysnames->nr++;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = size; /* consume everything, always */
|
|
|
+out:
|
|
|
+ inode_unlock(file_inode(file));
|
|
|
+ kfree(kbuf);
|
|
|
+ return ret;
|
|
|
+
|
|
|
+invalid:
|
|
|
+ ret = -EINVAL;
|
|
|
+error:
|
|
|
+ sysnames->error = ret;
|
|
|
+ goto out;
|
|
|
+}
|
|
|
+
|
|
|
+static int afs_proc_sysname_release(struct inode *inode, struct file *file)
|
|
|
+{
|
|
|
+ struct afs_sysnames *sysnames, *kill = NULL;
|
|
|
+ struct seq_file *m = file->private_data;
|
|
|
+ struct afs_net *net = afs_seq2net(m);
|
|
|
+
|
|
|
+ sysnames = m->private;
|
|
|
+ if (sysnames) {
|
|
|
+ if (!sysnames->error) {
|
|
|
+ kill = sysnames;
|
|
|
+ if (sysnames->nr == 0) {
|
|
|
+ sysnames->subs[0] = sysnames->blank;
|
|
|
+ sysnames->nr++;
|
|
|
+ }
|
|
|
+ write_lock(&net->sysnames_lock);
|
|
|
+ kill = net->sysnames;
|
|
|
+ net->sysnames = sysnames;
|
|
|
+ write_unlock(&net->sysnames_lock);
|
|
|
+ }
|
|
|
+ afs_put_sysnames(kill);
|
|
|
+ }
|
|
|
+
|
|
|
+ return seq_release(inode, file);
|
|
|
+}
|
|
|
+
|
|
|
+static void *afs_proc_sysname_start(struct seq_file *m, loff_t *pos)
|
|
|
+ __acquires(&net->sysnames_lock)
|
|
|
+{
|
|
|
+ struct afs_net *net = afs_seq2net(m);
|
|
|
+ struct afs_sysnames *names = net->sysnames;
|
|
|
+
|
|
|
+ read_lock(&net->sysnames_lock);
|
|
|
+
|
|
|
+ if (*pos >= names->nr)
|
|
|
+ return NULL;
|
|
|
+ return (void *)(unsigned long)(*pos + 1);
|
|
|
+}
|
|
|
+
|
|
|
+static void *afs_proc_sysname_next(struct seq_file *m, void *v, loff_t *pos)
|
|
|
+{
|
|
|
+ struct afs_net *net = afs_seq2net(m);
|
|
|
+ struct afs_sysnames *names = net->sysnames;
|
|
|
+
|
|
|
+ *pos += 1;
|
|
|
+ if (*pos >= names->nr)
|
|
|
+ return NULL;
|
|
|
+ return (void *)(unsigned long)(*pos + 1);
|
|
|
+}
|
|
|
+
|
|
|
+static void afs_proc_sysname_stop(struct seq_file *m, void *v)
|
|
|
+ __releases(&net->sysnames_lock)
|
|
|
+{
|
|
|
+ struct afs_net *net = afs_seq2net(m);
|
|
|
+
|
|
|
+ read_unlock(&net->sysnames_lock);
|
|
|
+}
|
|
|
+
|
|
|
+static int afs_proc_sysname_show(struct seq_file *m, void *v)
|
|
|
+{
|
|
|
+ struct afs_net *net = afs_seq2net(m);
|
|
|
+ struct afs_sysnames *sysnames = net->sysnames;
|
|
|
+ unsigned int i = (unsigned long)v - 1;
|
|
|
+
|
|
|
+ if (i < sysnames->nr)
|
|
|
+ seq_printf(m, "%s\n", sysnames->subs[i]);
|
|
|
+ return 0;
|
|
|
+}
|