#include "9p-xattr.h"
#include "coth.h"
#include "trace.h"
-#include "migration/migration.h"
+#include "migration/blocker.h"
int open_fd_hw;
int total_open_fd;
return ret;
}
-static void pdu_push_and_notify(V9fsPDU *pdu)
-{
- pdu->s->transport->push_and_notify(pdu);
-}
-
static int omode_to_uflags(int8_t mode)
{
int ret = 0;
/* Free all fids */
while (s->fid_list) {
+ /* Get fid */
fidp = s->fid_list;
+ fidp->ref++;
+
+ /* Clunk fid */
s->fid_list = fidp->next;
+ fidp->clunked = 1;
- if (fidp->ref) {
- fidp->clunked = 1;
- } else {
- free_fid(pdu, fidp);
- }
+ put_fid(pdu, fidp);
}
}
QLIST_INSERT_HEAD(&s->free_list, pdu, next);
}
-/*
- * We don't do error checking for pdu_marshal/unmarshal here
- * because we always expect to have enough space to encode
- * error details
- */
static void coroutine_fn pdu_complete(V9fsPDU *pdu, ssize_t len)
{
int8_t id = pdu->id + 1; /* Response */
V9fsState *s = pdu->s;
+ int ret;
if (len < 0) {
int err = -len;
str.data = strerror(err);
str.size = strlen(str.data);
- len += pdu_marshal(pdu, len, "s", &str);
+ ret = pdu_marshal(pdu, len, "s", &str);
+ if (ret < 0) {
+ goto out_notify;
+ }
+ len += ret;
id = P9_RERROR;
}
- len += pdu_marshal(pdu, len, "d", err);
+ ret = pdu_marshal(pdu, len, "d", err);
+ if (ret < 0) {
+ goto out_notify;
+ }
+ len += ret;
if (s->proto_version == V9FS_PROTO_2000L) {
id = P9_RLERROR;
}
/* fill out the header */
- pdu_marshal(pdu, 0, "dbw", (int32_t)len, id, pdu->tag);
+ if (pdu_marshal(pdu, 0, "dbw", (int32_t)len, id, pdu->tag) < 0) {
+ goto out_notify;
+ }
/* keep these in sync */
pdu->size = len;
pdu->id = id;
- pdu_push_and_notify(pdu);
+out_notify:
+ pdu->s->transport->push_and_notify(pdu);
/* Now wakeup anybody waiting in flush for this request */
if (!qemu_co_queue_next(&pdu->complete)) {
size_t offset = 7;
V9fsQID qid;
ssize_t err;
+ Error *local_err = NULL;
v9fs_string_init(&uname);
v9fs_string_init(&aname);
clunk_fid(s, fid);
goto out;
}
- err = pdu_marshal(pdu, offset, "Q", &qid);
- if (err < 0) {
- clunk_fid(s, fid);
- goto out;
- }
- err += offset;
- memcpy(&s->root_qid, &qid, sizeof(qid));
- trace_v9fs_attach_return(pdu->tag, pdu->id,
- qid.type, qid.version, qid.path);
+
/*
* disable migration if we haven't done already.
* attach could get called multiple times for the same export.
*/
if (!s->migration_blocker) {
- s->root_fid = fid;
error_setg(&s->migration_blocker,
"Migration is disabled when VirtFS export path '%s' is mounted in the guest using mount_tag '%s'",
s->ctx.fs_root ? s->ctx.fs_root : "NULL", s->tag);
- migrate_add_blocker(s->migration_blocker);
+ err = migrate_add_blocker(s->migration_blocker, &local_err);
+ if (local_err) {
+ error_free(local_err);
+ error_free(s->migration_blocker);
+ s->migration_blocker = NULL;
+ clunk_fid(s, fid);
+ goto out;
+ }
+ s->root_fid = fid;
}
+
+ err = pdu_marshal(pdu, offset, "Q", &qid);
+ if (err < 0) {
+ clunk_fid(s, fid);
+ goto out;
+ }
+ err += offset;
+
+ memcpy(&s->root_qid, &qid, sizeof(qid));
+ trace_v9fs_attach_return(pdu->tag, pdu->id,
+ qid.type, qid.version, qid.path);
out:
put_fid(pdu, fidp);
out_nofid:
err = -ENOENT;
goto out_nofid;
}
+ if (fidp->fid_type != P9_FID_NONE) {
+ err = -EINVAL;
+ goto out;
+ }
flags = get_dotl_openflags(pdu->s, flags);
err = v9fs_co_open2(pdu, fidp, &name, gid,
v9fs_string_free(&name);
}
-static void v9fs_fsync(void *opaque)
+static void coroutine_fn v9fs_fsync(void *opaque)
{
int err;
int32_t fid;
unsigned int niov;
if (is_write) {
- pdu->s->transport->init_out_iov_from_pdu(pdu, &iov, &niov);
+ pdu->s->transport->init_out_iov_from_pdu(pdu, &iov, &niov, size + skip);
} else {
- pdu->s->transport->init_in_iov_from_pdu(pdu, &iov, &niov, size);
+ pdu->s->transport->init_in_iov_from_pdu(pdu, &iov, &niov, size + skip);
}
qemu_iovec_init_external(&elem, iov, niov);
}
offset += err;
- v9fs_init_qiov_from_pdu(&qiov_full, pdu, 0, read_count, false);
- err = v9fs_pack(qiov_full.iov, qiov_full.niov, offset,
+ v9fs_init_qiov_from_pdu(&qiov_full, pdu, offset, read_count, false);
+ err = v9fs_pack(qiov_full.iov, qiov_full.niov, 0,
((char *)fidp->fs.xattr.value) + off,
read_count);
qemu_iovec_destroy(&qiov_full);
err = -EINVAL;
goto out_nofid;
}
+ if (fidp->fid_type != P9_FID_NONE) {
+ err = -EINVAL;
+ goto out;
+ }
if (perm & P9_STAT_MODE_DIR) {
err = v9fs_co_mkdir(pdu, fidp, &name, perm & 0777,
fidp->uid, -1, &stbuf);
v9fs_string_free(&symname);
}
-static void v9fs_flush(void *opaque)
+static void coroutine_fn v9fs_flush(void *opaque)
{
ssize_t err;
int16_t tag;
size_t offset = 7;
- V9fsPDU *cancel_pdu;
+ V9fsPDU *cancel_pdu = NULL;
V9fsPDU *pdu = opaque;
V9fsState *s = pdu->s;
}
trace_v9fs_flush(pdu->tag, pdu->id, tag);
- QLIST_FOREACH(cancel_pdu, &s->active_list, next) {
- if (cancel_pdu->tag == tag) {
- break;
+ if (pdu->tag == tag) {
+ error_report("Warning: the guest sent a self-referencing 9P flush request");
+ } else {
+ QLIST_FOREACH(cancel_pdu, &s->active_list, next) {
+ if (cancel_pdu->tag == tag) {
+ break;
+ }
}
}
if (cancel_pdu) {
/*
* Wait for pdu to complete.
*/
- qemu_co_queue_wait(&cancel_pdu->complete);
- cancel_pdu->cancelled = 0;
- pdu_free(cancel_pdu);
+ qemu_co_queue_wait(&cancel_pdu->complete, NULL);
+ if (!qemu_co_queue_next(&cancel_pdu->complete)) {
+ cancel_pdu->cancelled = 0;
+ pdu_free(cancel_pdu);
+ }
}
pdu_complete(pdu, 7);
}
err = -EINVAL;
goto out;
}
- v9fs_co_name_to_path(pdu, &dirfidp->path, name->data, &new_path);
+ err = v9fs_co_name_to_path(pdu, &dirfidp->path, name->data, &new_path);
+ if (err < 0) {
+ goto out;
+ }
} else {
old_name = fidp->path.data;
end = strrchr(old_name, '/');
new_name = g_malloc0(end - old_name + name->size + 1);
strncat(new_name, old_name, end - old_name);
strncat(new_name + (end - old_name), name->data, name->size);
- v9fs_co_name_to_path(pdu, NULL, new_name, &new_path);
+ err = v9fs_co_name_to_path(pdu, NULL, new_name, &new_path);
g_free(new_name);
+ if (err < 0) {
+ goto out;
+ }
}
err = v9fs_co_rename(pdu, &fidp->path, &new_path);
if (err < 0) {
v9fs_string_free(&name);
}
-static void coroutine_fn v9fs_fix_fid_paths(V9fsPDU *pdu, V9fsPath *olddir,
- V9fsString *old_name,
- V9fsPath *newdir,
- V9fsString *new_name)
+static int coroutine_fn v9fs_fix_fid_paths(V9fsPDU *pdu, V9fsPath *olddir,
+ V9fsString *old_name,
+ V9fsPath *newdir,
+ V9fsString *new_name)
{
V9fsFidState *tfidp;
V9fsPath oldpath, newpath;
V9fsState *s = pdu->s;
-
+ int err;
v9fs_path_init(&oldpath);
v9fs_path_init(&newpath);
- v9fs_co_name_to_path(pdu, olddir, old_name->data, &oldpath);
- v9fs_co_name_to_path(pdu, newdir, new_name->data, &newpath);
+ err = v9fs_co_name_to_path(pdu, olddir, old_name->data, &oldpath);
+ if (err < 0) {
+ goto out;
+ }
+ err = v9fs_co_name_to_path(pdu, newdir, new_name->data, &newpath);
+ if (err < 0) {
+ goto out;
+ }
/*
* Fixup fid's pointing to the old name to
v9fs_fix_path(&tfidp->path, &newpath, strlen(oldpath.data));
}
}
+out:
v9fs_path_free(&oldpath);
v9fs_path_free(&newpath);
+ return err;
}
static int coroutine_fn v9fs_complete_renameat(V9fsPDU *pdu, int32_t olddirfid,
}
if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) {
/* Only for path based fid we need to do the below fixup */
- v9fs_fix_fid_paths(pdu, &olddirfidp->path, old_name,
- &newdirfidp->path, new_name);
+ err = v9fs_fix_fid_paths(pdu, &olddirfidp->path, old_name,
+ &newdirfidp->path, new_name);
}
out:
if (olddirfidp) {
*/
static void coroutine_fn v9fs_lock(void *opaque)
{
- int8_t status;
V9fsFlock flock;
size_t offset = 7;
struct stat stbuf;
int32_t fid, err = 0;
V9fsPDU *pdu = opaque;
- status = P9_LOCK_ERROR;
v9fs_string_init(&flock.client_id);
err = pdu_unmarshal(pdu, offset, "dbdqqds", &fid, &flock.type,
&flock.flags, &flock.start, &flock.length,
if (err < 0) {
goto out;
}
- status = P9_LOCK_SUCCESS;
+ err = pdu_marshal(pdu, offset, "b", P9_LOCK_SUCCESS);
+ if (err < 0) {
+ goto out;
+ }
+ err += offset;
+ trace_v9fs_lock_return(pdu->tag, pdu->id, P9_LOCK_SUCCESS);
out:
put_fid(pdu, fidp);
out_nofid:
- err = pdu_marshal(pdu, offset, "b", status);
- if (err > 0) {
- err += offset;
- }
- trace_v9fs_lock_return(pdu->tag, pdu->id, status);
pdu_complete(pdu, err);
v9fs_string_free(&flock.client_id);
}
}
}
-void pdu_submit(V9fsPDU *pdu)
+void pdu_submit(V9fsPDU *pdu, P9MsgHeader *hdr)
{
Coroutine *co;
CoroutineEntry *handler;
V9fsState *s = pdu->s;
+ pdu->size = le32_to_cpu(hdr->size_le);
+ pdu->id = hdr->id;
+ pdu->tag = le16_to_cpu(hdr->tag_le);
+
if (pdu->id >= ARRAY_SIZE(pdu_co_handlers) ||
(pdu_co_handlers[pdu->id] == NULL)) {
handler = v9fs_op_not_supp;
if (is_ro_export(&s->ctx) && !is_read_only_op(pdu)) {
handler = v9fs_fs_ro;
}
+
+ qemu_co_queue_init(&pdu->complete);
co = qemu_coroutine_create(handler, pdu);
qemu_coroutine_enter(co);
}
/* initialize pdu allocator */
QLIST_INIT(&s->free_list);
QLIST_INIT(&s->active_list);
- for (i = 0; i < (MAX_REQ - 1); i++) {
+ for (i = 0; i < MAX_REQ; i++) {
QLIST_INSERT_HEAD(&s->free_list, &s->pdus[i], next);
s->pdus[i].s = s;
s->pdus[i].idx = i;
s->ops = fse->ops;
+ s->ctx.fmode = fse->fmode;
+ s->ctx.dmode = fse->dmode;
+
s->fid_list = NULL;
qemu_co_rwlock_init(&s->rename_lock);
error_setg(errp, "share path %s is not a directory", fse->path);
goto out;
}
+
+ s->ctx.fst = &fse->fst;
+ fsdev_throttle_init(s->ctx.fst);
+
v9fs_path_free(&path);
rc = 0;
if (s->ops->cleanup) {
s->ops->cleanup(&s->ctx);
}
+ fsdev_throttle_cleanup(s->ctx.fst);
g_free(s->tag);
g_free(s->ctx.fs_root);
}