mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-27 13:30:52 +00:00
9p: add locking to V9fsDir
If several threads concurrently call readdir() with the same directory stream pointer, it is possible that they all get a pointer to the same dirent structure, whose content is overwritten each time readdir() is called. We must thus serialize accesses to the dirent structure. This may be achieved with a mutex like below: lock_mutex(); readdir(); // work with the dirent unlock_mutex(); This patch adds all the locking, to prepare the switch to readdir(). Reviewed-by: Eric Blake <eblake@redhat.com> Signed-off-by: Greg Kurz <gkurz@linux.vnet.ibm.com>
This commit is contained in:
parent
f314ea4e30
commit
7cde47d4a8
21
hw/9pfs/9p.c
21
hw/9pfs/9p.c
@ -300,6 +300,9 @@ static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid)
|
|||||||
f->next = s->fid_list;
|
f->next = s->fid_list;
|
||||||
s->fid_list = f;
|
s->fid_list = f;
|
||||||
|
|
||||||
|
v9fs_readdir_init(&f->fs.dir);
|
||||||
|
v9fs_readdir_init(&f->fs_reclaim.dir);
|
||||||
|
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1636,6 +1639,9 @@ static int v9fs_do_readdir_with_stat(V9fsPDU *pdu,
|
|||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
v9fs_path_init(&path);
|
v9fs_path_init(&path);
|
||||||
|
|
||||||
|
v9fs_readdir_lock(&fidp->fs.dir);
|
||||||
|
|
||||||
err = v9fs_co_readdir_r(pdu, fidp, dent, &result);
|
err = v9fs_co_readdir_r(pdu, fidp, dent, &result);
|
||||||
if (err || !result) {
|
if (err || !result) {
|
||||||
break;
|
break;
|
||||||
@ -1654,6 +1660,9 @@ static int v9fs_do_readdir_with_stat(V9fsPDU *pdu,
|
|||||||
}
|
}
|
||||||
/* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */
|
/* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */
|
||||||
len = pdu_marshal(pdu, 11 + count, "S", &v9stat);
|
len = pdu_marshal(pdu, 11 + count, "S", &v9stat);
|
||||||
|
|
||||||
|
v9fs_readdir_unlock(&fidp->fs.dir);
|
||||||
|
|
||||||
if ((len != (v9stat.size + 2)) || ((count + len) > max_count)) {
|
if ((len != (v9stat.size + 2)) || ((count + len) > max_count)) {
|
||||||
/* Ran out of buffer. Set dir back to old position and return */
|
/* Ran out of buffer. Set dir back to old position and return */
|
||||||
v9fs_co_seekdir(pdu, fidp, saved_dir_pos);
|
v9fs_co_seekdir(pdu, fidp, saved_dir_pos);
|
||||||
@ -1668,6 +1677,8 @@ static int v9fs_do_readdir_with_stat(V9fsPDU *pdu,
|
|||||||
saved_dir_pos = dent->d_off;
|
saved_dir_pos = dent->d_off;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v9fs_readdir_unlock(&fidp->fs.dir);
|
||||||
|
|
||||||
g_free(dent);
|
g_free(dent);
|
||||||
v9fs_path_free(&path);
|
v9fs_path_free(&path);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
@ -1815,6 +1826,8 @@ static int v9fs_do_readdir(V9fsPDU *pdu,
|
|||||||
dent = g_malloc(sizeof(struct dirent));
|
dent = g_malloc(sizeof(struct dirent));
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
v9fs_readdir_lock(&fidp->fs.dir);
|
||||||
|
|
||||||
err = v9fs_co_readdir_r(pdu, fidp, dent, &result);
|
err = v9fs_co_readdir_r(pdu, fidp, dent, &result);
|
||||||
if (err || !result) {
|
if (err || !result) {
|
||||||
break;
|
break;
|
||||||
@ -1822,6 +1835,8 @@ static int v9fs_do_readdir(V9fsPDU *pdu,
|
|||||||
v9fs_string_init(&name);
|
v9fs_string_init(&name);
|
||||||
v9fs_string_sprintf(&name, "%s", dent->d_name);
|
v9fs_string_sprintf(&name, "%s", dent->d_name);
|
||||||
if ((count + v9fs_readdir_data_size(&name)) > max_count) {
|
if ((count + v9fs_readdir_data_size(&name)) > max_count) {
|
||||||
|
v9fs_readdir_unlock(&fidp->fs.dir);
|
||||||
|
|
||||||
/* Ran out of buffer. Set dir back to old position and return */
|
/* Ran out of buffer. Set dir back to old position and return */
|
||||||
v9fs_co_seekdir(pdu, fidp, saved_dir_pos);
|
v9fs_co_seekdir(pdu, fidp, saved_dir_pos);
|
||||||
v9fs_string_free(&name);
|
v9fs_string_free(&name);
|
||||||
@ -1843,6 +1858,9 @@ static int v9fs_do_readdir(V9fsPDU *pdu,
|
|||||||
len = pdu_marshal(pdu, 11 + count, "Qqbs",
|
len = pdu_marshal(pdu, 11 + count, "Qqbs",
|
||||||
&qid, dent->d_off,
|
&qid, dent->d_off,
|
||||||
dent->d_type, &name);
|
dent->d_type, &name);
|
||||||
|
|
||||||
|
v9fs_readdir_unlock(&fidp->fs.dir);
|
||||||
|
|
||||||
if (len < 0) {
|
if (len < 0) {
|
||||||
v9fs_co_seekdir(pdu, fidp, saved_dir_pos);
|
v9fs_co_seekdir(pdu, fidp, saved_dir_pos);
|
||||||
v9fs_string_free(&name);
|
v9fs_string_free(&name);
|
||||||
@ -1853,6 +1871,9 @@ static int v9fs_do_readdir(V9fsPDU *pdu,
|
|||||||
v9fs_string_free(&name);
|
v9fs_string_free(&name);
|
||||||
saved_dir_pos = dent->d_off;
|
saved_dir_pos = dent->d_off;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v9fs_readdir_unlock(&fidp->fs.dir);
|
||||||
|
|
||||||
g_free(dent);
|
g_free(dent);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
return err;
|
return err;
|
||||||
|
16
hw/9pfs/9p.h
16
hw/9pfs/9p.h
@ -169,8 +169,24 @@ typedef struct V9fsXattr
|
|||||||
|
|
||||||
typedef struct V9fsDir {
|
typedef struct V9fsDir {
|
||||||
DIR *stream;
|
DIR *stream;
|
||||||
|
QemuMutex readdir_mutex;
|
||||||
} V9fsDir;
|
} V9fsDir;
|
||||||
|
|
||||||
|
static inline void v9fs_readdir_lock(V9fsDir *dir)
|
||||||
|
{
|
||||||
|
qemu_mutex_lock(&dir->readdir_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void v9fs_readdir_unlock(V9fsDir *dir)
|
||||||
|
{
|
||||||
|
qemu_mutex_unlock(&dir->readdir_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void v9fs_readdir_init(V9fsDir *dir)
|
||||||
|
{
|
||||||
|
qemu_mutex_init(&dir->readdir_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Filled by fs driver on open and other
|
* Filled by fs driver on open and other
|
||||||
* calls.
|
* calls.
|
||||||
|
Loading…
Reference in New Issue
Block a user