{
int name_idx, nwalked;
g_autofree V9fsQID *qids = NULL;
- int i, err = 0;
+ int i, err = 0, any_err = 0;
V9fsPath dpath, path;
P9ARRAY_REF(V9fsPath) pathes = NULL;
uint16_t nwnames;
* driver code altogether inside the following block.
*/
v9fs_co_run_in_worker({
+ nwalked = 0;
if (v9fs_request_cancelled(pdu)) {
- err = -EINTR;
+ any_err |= err = -EINTR;
break;
}
err = s->ops->lstat(&s->ctx, &dpath, &fidst);
if (err < 0) {
- err = -errno;
+ any_err |= err = -errno;
break;
}
stbuf = fidst;
- for (nwalked = 0; nwalked < nwnames; nwalked++) {
+ for (; nwalked < nwnames; nwalked++) {
if (v9fs_request_cancelled(pdu)) {
- err = -EINTR;
+ any_err |= err = -EINTR;
break;
}
if (!same_stat_id(&pdu->s->root_st, &stbuf) ||
wnames[nwalked].data,
&pathes[nwalked]);
if (err < 0) {
- err = -errno;
+ any_err |= err = -errno;
break;
}
if (v9fs_request_cancelled(pdu)) {
- err = -EINTR;
+ any_err |= err = -EINTR;
break;
}
err = s->ops->lstat(&s->ctx, &pathes[nwalked], &stbuf);
if (err < 0) {
- err = -errno;
+ any_err |= err = -errno;
break;
}
stbufs[nwalked] = stbuf;
});
/*
* Handle all the rest of this Twalk request on main thread ...
+ *
+ * NOTE: -EINTR is an exception where we deviate from the protocol spec
+ * and simply send a (R)Lerror response instead of bothering to assemble
+ * a (deducted) Rwalk response; because -EINTR is always the result of a
+ * Tflush request, so client would no longer wait for a response in this
+ * case anyway.
*/
- if (err < 0) {
+ if ((err < 0 && !nwalked) || err == -EINTR) {
goto out;
}
- err = stat_to_qid(pdu, &fidst, &qid);
- if (err < 0) {
+ any_err |= err = stat_to_qid(pdu, &fidst, &qid);
+ if (err < 0 && !nwalked) {
goto out;
}
stbuf = fidst;
v9fs_path_copy(&dpath, &fidp->path);
v9fs_path_copy(&path, &fidp->path);
- for (name_idx = 0; name_idx < nwnames; name_idx++) {
+ for (name_idx = 0; name_idx < nwalked; name_idx++) {
if (!same_stat_id(&pdu->s->root_st, &stbuf) ||
strcmp("..", wnames[name_idx].data))
{
stbuf = stbufs[name_idx];
- err = stat_to_qid(pdu, &stbuf, &qid);
+ any_err |= err = stat_to_qid(pdu, &stbuf, &qid);
if (err < 0) {
- goto out;
+ break;
}
v9fs_path_copy(&path, &pathes[name_idx]);
v9fs_path_copy(&dpath, &path);
}
memcpy(&qids[name_idx], &qid, sizeof(qid));
}
+ if (any_err < 0) {
+ if (!name_idx) {
+ /* don't send any QIDs, send Rlerror instead */
+ goto out;
+ } else {
+ /* send QIDs (not Rlerror), but fid MUST remain unaffected */
+ goto send_qids;
+ }
+ }
if (fid == newfid) {
if (fidp->fid_type != P9_FID_NONE) {
err = -EINVAL;
newfidp->uid = fidp->uid;
v9fs_path_copy(&newfidp->path, &path);
}
- err = v9fs_walk_marshal(pdu, nwnames, qids);
- trace_v9fs_walk_return(pdu->tag, pdu->id, nwnames, qids);
+send_qids:
+ err = v9fs_walk_marshal(pdu, name_idx, qids);
+ trace_v9fs_walk_return(pdu->tag, pdu->id, name_idx, qids);
out:
put_fid(pdu, fidp);
if (newfidp) {