// SPDX-License-Identifier: GPL-2.0-or-later /* Address preferences management * * Copyright (C) 2023 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com)
*/
/* * Split a NUL-terminated string up to the first newline around spaces. The * source string will be modified to have NUL-terminations inserted.
*/ staticint afs_split_string(char **pbuf, char *strv[], unsignedint maxstrv)
{ unsignedint count = 0; char *p = *pbuf;
maxstrv--; /* Allow for terminal NULL */ for (;;) { /* Skip over spaces */ while (isspace(*p)) { if (*p == '\n') {
p++; break;
}
p++;
} if (!*p) break;
/* Mark start of word */ if (count >= maxstrv) {
pr_warn("Too many elements in string\n"); return -EINVAL;
}
strv[count++] = p;
/* Skip over word */ while (!isspace(*p) && *p)
p++; if (!*p) break;
/* Mark end of word */ if (*p == '\n') {
*p++ = 0; break;
}
*p++ = 0;
}
*pbuf = p;
strv[count] = NULL; return count;
}
/* * Parse an address with an optional subnet mask.
*/ staticint afs_parse_address(char *p, struct afs_addr_preference *pref)
{ constchar *stop; unsignedlong mask, tmp; char *end = p + strlen(p); bool bracket = false;
if (*p == '[') {
p++;
bracket = true;
}
#if 0 if (*p == '[') {
p++;
q = memchr(p, ']', end - p); if (!q) {
pr_warn("Can't find closing ']'\n"); return -EINVAL;
}
} else { for (q = p; q < end; q++) if (*q == '/') break;
} #endif
ret = afs_parse_address(argv[1], &pref); if (ret < 0) return ret;
if (pref.family == AF_INET) {
i = 0;
stop = preflist->ipv6_off;
} else {
i = preflist->ipv6_off;
stop = preflist->nr;
}
for (; i < stop; i++) {
cmp = afs_cmp_address_pref(&pref, &preflist->prefs[i]); switch (cmp) { case CONTINUE_SEARCH: continue; case INSERT_HERE: case SUBNET_MATCH: return 0; case EXACT_MATCH: return afs_delete_address_pref(_preflist, i);
}
}
/* Allocate a candidate new list and initialise it from the old. */
old = rcu_dereference_protected(net->address_prefs,
lockdep_is_held(&file_inode(file)->i_rwsem));
do {
argc = afs_split_string(&buf, argv, ARRAY_SIZE(argv)); if (argc < 0) {
ret = argc; goto done;
} if (argc < 2) goto inval;
if (strcmp(argv[0], "add") == 0)
ret = afs_add_address_pref(net, &preflist, argc - 1, argv + 1); elseif (strcmp(argv[0], "del") == 0)
ret = afs_del_address_pref(net, &preflist, argc - 1, argv + 1); else goto inval; if (ret < 0) goto done;
} while (*buf);
preflist->version++;
rcu_assign_pointer(net->address_prefs, preflist); /* Store prefs before version */
smp_store_release(&net->address_pref_version, preflist->version);
kfree_rcu(old, rcu);
preflist = NULL;
ret = 0;
inval:
pr_warn("Invalid Command\n");
ret = -EINVAL; goto done;
}
/* * Mark the priorities on an address list if the address preferences table has * changed. The caller must hold the RCU read lock.
*/ void afs_get_address_preferences_rcu(struct afs_net *net, struct afs_addr_list *alist)
{ conststruct afs_addr_preference_list *preflist =
rcu_dereference(net->address_prefs); conststruct sockaddr_in6 *sin6; conststruct sockaddr_in *sin; conststruct sockaddr *sa; struct afs_addr_preference test; enum cmp_ret cmp; int i, j;
/* * Mark the priorities on an address list if the address preferences table has * changed. Avoid taking the RCU read lock if we can.
*/ void afs_get_address_preferences(struct afs_net *net, struct afs_addr_list *alist)
{ if (!net->address_prefs || /* Load version before prefs */
smp_load_acquire(&net->address_pref_version) == alist->addr_pref_version) return;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.