diff options
author | Greg Kurz <groug@kaod.org> | 2016-08-30 17:02:27 +0200 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2016-08-30 19:23:00 +0100 |
commit | 56f101ecce0eafd09e2daf1c4eeb1377d6959261 (patch) | |
tree | 9b7c8ceb972c7a4da49bee92fff49939376e35a1 /hw/9pfs | |
parent | 805b5d98c649d26fc44d2d7755a97f18e62b438a (diff) |
9pfs: handle walk of ".." in the root directory
The 9P spec at http://man.cat-v.org/plan_9/5/intro says:
All directories must support walks to the directory .. (dot-dot) meaning
parent directory, although by convention directories contain no explicit
entry for .. or . (dot). The parent of the root directory of a server's
tree is itself.
This means that a client cannot walk further than the root directory
exported by the server. In other words, if the client wants to walk
"/.." or "/foo/../..", the server should answer like the request was
to walk "/".
This patch just does that:
- we cache the QID of the root directory at attach time
- during the walk we compare the QID of each path component with the root
QID to detect if we're in a "/.." situation
- if so, we skip the current component and go to the next one
Signed-off-by: Greg Kurz <groug@kaod.org>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/9pfs')
-rw-r--r-- | hw/9pfs/9p.c | 40 | ||||
-rw-r--r-- | hw/9pfs/9p.h | 1 |
2 files changed, 32 insertions, 9 deletions
diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 51c6f9883b..dfe293d11d 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -1010,6 +1010,7 @@ static void v9fs_attach(void *opaque) 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); /* @@ -1261,6 +1262,14 @@ static bool name_is_illegal(const char *name) return !*name || strchr(name, '/') != NULL; } +static bool not_same_qid(const V9fsQID *qid1, const V9fsQID *qid2) +{ + return + qid1->type != qid2->type || + qid1->version != qid2->version || + qid1->path != qid2->path; +} + static void v9fs_walk(void *opaque) { int name_idx; @@ -1276,6 +1285,7 @@ static void v9fs_walk(void *opaque) V9fsFidState *newfidp = NULL; V9fsPDU *pdu = opaque; V9fsState *s = pdu->s; + V9fsQID qid; err = pdu_unmarshal(pdu, offset, "ddw", &fid, &newfid, &nwnames); if (err < 0) { @@ -1309,6 +1319,12 @@ static void v9fs_walk(void *opaque) err = -ENOENT; goto out_nofid; } + + err = fid_to_qid(pdu, fidp, &qid); + if (err < 0) { + goto out; + } + v9fs_path_init(&dpath); v9fs_path_init(&path); /* @@ -1318,16 +1334,22 @@ static void v9fs_walk(void *opaque) v9fs_path_copy(&dpath, &fidp->path); v9fs_path_copy(&path, &fidp->path); for (name_idx = 0; name_idx < nwnames; name_idx++) { - err = v9fs_co_name_to_path(pdu, &dpath, wnames[name_idx].data, &path); - if (err < 0) { - goto out; - } - err = v9fs_co_lstat(pdu, &path, &stbuf); - if (err < 0) { - goto out; + if (not_same_qid(&pdu->s->root_qid, &qid) || + strcmp("..", wnames[name_idx].data)) { + err = v9fs_co_name_to_path(pdu, &dpath, wnames[name_idx].data, + &path); + if (err < 0) { + goto out; + } + + err = v9fs_co_lstat(pdu, &path, &stbuf); + if (err < 0) { + goto out; + } + stat_to_qid(&stbuf, &qid); + v9fs_path_copy(&dpath, &path); } - stat_to_qid(&stbuf, &qids[name_idx]); - v9fs_path_copy(&dpath, &path); + memcpy(&qids[name_idx], &qid, sizeof(qid)); } if (fid == newfid) { BUG_ON(fidp->fid_type != P9_FID_NONE); diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h index b4f757ab54..a38603398e 100644 --- a/hw/9pfs/9p.h +++ b/hw/9pfs/9p.h @@ -236,6 +236,7 @@ typedef struct V9fsState int32_t root_fid; Error *migration_blocker; V9fsConf fsconf; + V9fsQID root_qid; } V9fsState; /* 9p2000.L open flags */ |