mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 10:40:03 +01:00
With the addition of FMODE_ATOMIC_POS in the Linux 3.14 kernel, concurrent blocking file accesses to a single open file descriptor can cause a deadlock trying to grab the file position lock. If a watch has been set up, causing a read_thread to blocking read on the file descriptor, then future writes that would cause the background read to complete will block waiting on the file position lock before they can execute. This race condition only occurs when libxenstore is accessing the xenstore daemon through the /proc/xen/xenbus file and not through the unix domain socket, which is the case when the xenstore daemon is running as a stub domain or when oxenstored is passed --disable-socket. Accessing the daemon from the true character device also does not exhibit this problem. On Linux, prefer using the character device file over the proc file if the character device exists. Signed-off-by: Jonathan Creekmore <jonathan.creekmore@gmail.com> Acked-by: Wei Liu <wei.liu2@citrix.com> Acked-by: Ian Campbell <ian.campbell@citrix.com>
298 lines
6.2 KiB
C
298 lines
6.2 KiB
C
/*
|
|
Common routines between Xen store user library and daemon.
|
|
Copyright (C) 2005 Rusty Russell IBM Corporation
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <assert.h>
|
|
#include "xenstore_lib.h"
|
|
|
|
/* Common routines for the Xen store daemon and client library. */
|
|
|
|
const char *xs_daemon_rootdir(void)
|
|
{
|
|
char *s = getenv("XENSTORED_ROOTDIR");
|
|
return (s ? s : XEN_LIB_STORED);
|
|
}
|
|
|
|
const char *xs_daemon_rundir(void)
|
|
{
|
|
char *s = getenv("XENSTORED_RUNDIR");
|
|
return (s ? s : "/var/run/xenstored");
|
|
}
|
|
|
|
static const char *xs_daemon_path(void)
|
|
{
|
|
static char buf[PATH_MAX];
|
|
char *s = getenv("XENSTORED_PATH");
|
|
if (s)
|
|
return s;
|
|
if (snprintf(buf, sizeof(buf), "%s/socket",
|
|
xs_daemon_rundir()) >= PATH_MAX)
|
|
return NULL;
|
|
return buf;
|
|
}
|
|
|
|
const char *xs_daemon_tdb(void)
|
|
{
|
|
static char buf[PATH_MAX];
|
|
snprintf(buf, sizeof(buf), "%s/tdb", xs_daemon_rootdir());
|
|
return buf;
|
|
}
|
|
|
|
const char *xs_daemon_socket(void)
|
|
{
|
|
return xs_daemon_path();
|
|
}
|
|
|
|
const char *xs_daemon_socket_ro(void)
|
|
{
|
|
static char buf[PATH_MAX];
|
|
const char *s = xs_daemon_path();
|
|
if (s == NULL)
|
|
return NULL;
|
|
if (snprintf(buf, sizeof(buf), "%s_ro", s) >= PATH_MAX)
|
|
return NULL;
|
|
return buf;
|
|
}
|
|
|
|
const char *xs_domain_dev(void)
|
|
{
|
|
char *s = getenv("XENSTORED_PATH");
|
|
if (s)
|
|
return s;
|
|
#if defined(__RUMPUSER_XEN__) || defined(__RUMPRUN__)
|
|
return "/dev/xen/xenbus";
|
|
#elif defined(__linux__)
|
|
if (access("/dev/xen/xenbus", F_OK) == 0)
|
|
return "/dev/xen/xenbus";
|
|
return "/proc/xen/xenbus";
|
|
#elif defined(__NetBSD__)
|
|
return "/kern/xen/xenbus";
|
|
#else
|
|
return "/dev/xen/xenbus";
|
|
#endif
|
|
}
|
|
|
|
/* Simple routines for writing to sockets, etc. */
|
|
bool xs_write_all(int fd, const void *data, unsigned int len)
|
|
{
|
|
while (len) {
|
|
int done;
|
|
|
|
done = write(fd, data, len);
|
|
if (done < 0 && errno == EINTR)
|
|
continue;
|
|
if (done <= 0)
|
|
return false;
|
|
data += done;
|
|
len -= done;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Convert strings to permissions. False if a problem. */
|
|
bool xs_strings_to_perms(struct xs_permissions *perms, unsigned int num,
|
|
const char *strings)
|
|
{
|
|
const char *p;
|
|
char *end;
|
|
unsigned int i;
|
|
|
|
for (p = strings, i = 0; i < num; i++) {
|
|
/* "r", "w", or "b" for both. */
|
|
switch (*p) {
|
|
case 'r':
|
|
perms[i].perms = XS_PERM_READ;
|
|
break;
|
|
case 'w':
|
|
perms[i].perms = XS_PERM_WRITE;
|
|
break;
|
|
case 'b':
|
|
perms[i].perms = XS_PERM_READ|XS_PERM_WRITE;
|
|
break;
|
|
case 'n':
|
|
perms[i].perms = XS_PERM_NONE;
|
|
break;
|
|
default:
|
|
errno = EINVAL;
|
|
return false;
|
|
}
|
|
p++;
|
|
perms[i].id = strtol(p, &end, 0);
|
|
if (*end || !*p) {
|
|
errno = EINVAL;
|
|
return false;
|
|
}
|
|
p = end + 1;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* Convert permissions to a string (up to len MAX_STRLEN(unsigned int)+1). */
|
|
bool xs_perm_to_string(const struct xs_permissions *perm,
|
|
char *buffer, size_t buf_len)
|
|
{
|
|
switch ((int)perm->perms) {
|
|
case XS_PERM_WRITE:
|
|
*buffer = 'w';
|
|
break;
|
|
case XS_PERM_READ:
|
|
*buffer = 'r';
|
|
break;
|
|
case XS_PERM_READ|XS_PERM_WRITE:
|
|
*buffer = 'b';
|
|
break;
|
|
case XS_PERM_NONE:
|
|
*buffer = 'n';
|
|
break;
|
|
default:
|
|
errno = EINVAL;
|
|
return false;
|
|
}
|
|
snprintf(buffer+1, buf_len-1, "%i", (int)perm->id);
|
|
return true;
|
|
}
|
|
|
|
/* Given a string and a length, count how many strings (nul terms). */
|
|
unsigned int xs_count_strings(const char *strings, unsigned int len)
|
|
{
|
|
unsigned int num;
|
|
const char *p;
|
|
|
|
for (p = strings, num = 0; p < strings + len; p++)
|
|
if (*p == '\0')
|
|
num++;
|
|
|
|
return num;
|
|
}
|
|
|
|
char *expanding_buffer_ensure(struct expanding_buffer *ebuf, int min_avail)
|
|
{
|
|
int want;
|
|
char *got;
|
|
|
|
if (ebuf->avail >= min_avail)
|
|
return ebuf->buf;
|
|
|
|
if (min_avail >= INT_MAX/3)
|
|
return 0;
|
|
|
|
want = ebuf->avail + min_avail + 10;
|
|
got = realloc(ebuf->buf, want);
|
|
if (!got)
|
|
return 0;
|
|
|
|
ebuf->buf = got;
|
|
ebuf->avail = want;
|
|
return ebuf->buf;
|
|
}
|
|
|
|
char *sanitise_value(struct expanding_buffer *ebuf,
|
|
const char *val, unsigned len)
|
|
{
|
|
int used, remain, c;
|
|
unsigned char *ip;
|
|
|
|
#define ADD(c) (ebuf->buf[used++] = (c))
|
|
#define ADDF(f,c) (used += sprintf(ebuf->buf+used, (f), (c)))
|
|
|
|
assert(len < INT_MAX/5);
|
|
|
|
ip = (unsigned char *)val;
|
|
used = 0;
|
|
remain = len;
|
|
|
|
if (!expanding_buffer_ensure(ebuf, remain + 1))
|
|
return NULL;
|
|
|
|
while (remain-- > 0) {
|
|
c= *ip++;
|
|
|
|
if (c >= ' ' && c <= '~' && c != '\\') {
|
|
ADD(c);
|
|
continue;
|
|
}
|
|
|
|
if (!expanding_buffer_ensure(ebuf, used + remain + 5))
|
|
/* for "<used>\\nnn<remain>\0" */
|
|
return 0;
|
|
|
|
ADD('\\');
|
|
switch (c) {
|
|
case '\t': ADD('t'); break;
|
|
case '\n': ADD('n'); break;
|
|
case '\r': ADD('r'); break;
|
|
case '\\': ADD('\\'); break;
|
|
default:
|
|
if (c < 010) ADDF("%03o", c);
|
|
else ADDF("x%02x", c);
|
|
}
|
|
}
|
|
|
|
ADD(0);
|
|
assert(used <= ebuf->avail);
|
|
return ebuf->buf;
|
|
|
|
#undef ADD
|
|
#undef ADDF
|
|
}
|
|
|
|
void unsanitise_value(char *out, unsigned *out_len_r, const char *in)
|
|
{
|
|
const char *ip;
|
|
char *op;
|
|
unsigned c;
|
|
int n;
|
|
|
|
for (ip = in, op = out; (c = *ip++); *op++ = c) {
|
|
if (c == '\\') {
|
|
c = *ip++;
|
|
|
|
#define GETF(f) do { \
|
|
n = 0; \
|
|
sscanf(ip, f "%n", &c, &n); \
|
|
ip += n; \
|
|
} while (0)
|
|
|
|
switch (c) {
|
|
case 't': c= '\t'; break;
|
|
case 'n': c= '\n'; break;
|
|
case 'r': c= '\r'; break;
|
|
case '\\': c= '\\'; break;
|
|
case 'x': GETF("%2x"); break;
|
|
case '0': case '4':
|
|
case '1': case '5':
|
|
case '2': case '6':
|
|
case '3': case '7': --ip; GETF("%3o"); break;
|
|
case 0: --ip; break;
|
|
default:;
|
|
}
|
|
#undef GETF
|
|
}
|
|
}
|
|
|
|
*op = 0;
|
|
|
|
if (out_len_r)
|
|
*out_len_r = op - out;
|
|
}
|