diff options
Diffstat (limited to 'lib9p/srv.c')
-rw-r--r-- | lib9p/srv.c | 83 |
1 files changed, 75 insertions, 8 deletions
diff --git a/lib9p/srv.c b/lib9p/srv.c index 84b3ae8..5af0971 100644 --- a/lib9p/srv.c +++ b/lib9p/srv.c @@ -515,6 +515,7 @@ static void handle_Tattach(struct _lib9p_srv_req *ctx, } struct lib9p_srv_file *rootdir = srv->rootdir(&ctx->ctx, req->aname.utf8); + assert((rootdir == NULL) == lib9p_ctx_has_error(&ctx->ctx.basectx)); if (lib9p_ctx_has_error(&ctx->ctx.basectx)) return; rootdir->_parent_dir = rootdir; @@ -522,8 +523,7 @@ static void handle_Tattach(struct _lib9p_srv_req *ctx, if (!fidmap_store(&ctx->parent_sess->fids, req->fid, rootdir)) { lib9p_error(&ctx->ctx.basectx, LINUX_EMFILE, "too many open files"); - if (rootdir->vtable->free) - rootdir->vtable->free(&ctx->ctx, rootdir); + rootdir->vtable->free(&ctx->ctx, rootdir); return; } @@ -558,8 +558,70 @@ static void handle_Twalk(struct _lib9p_srv_req *ctx, assert(req); assert(resp); - lib9p_error(&ctx->ctx.basectx, - LINUX_EOPNOTSUPP, "walk not yet implemented"); + struct lib9p_srv_file **dirpp = fidmap_load(&ctx->parent_sess->fids, req->fid); + if (!dirpp) { + lib9p_errorf(&ctx->ctx.basectx, + LINUX_EBADF, "bad file number %"PRIu32, req->fid); + return; + } + if (req->newfid != req->fid && fidmap_load(&ctx->parent_sess->fids, req->newfid)) { + lib9p_error(&ctx->ctx.basectx, + LINUX_EBADF, "FID already in use"); + return; + } + + struct lib9p_srv_file *dirp; + if (req->newfid == req->fid) + dirp = *dirpp; + else { + dirp = (*dirpp)->vtable->clone(&ctx->ctx, *dirpp); + assert((dirp == NULL) == lib9p_ctx_has_error(&ctx->ctx.basectx)); + if (lib9p_ctx_has_error(&ctx->ctx.basectx)) + return; + } + + resp->wqid = (struct lib9p_qid *)(&resp[1]); + for (resp->nwqid = 0; resp->nwqid < req->nwname; resp->nwqid++) { + struct lib9p_srv_file *memberp; + if (strcmp(req->wname[resp->nwqid].utf8, "..")) { + memberp = dirp->_parent_dir; + } else { + struct lib9p_stat stat = dirp->vtable->stat(&ctx->ctx, dirp); + if (lib9p_ctx_has_error(&ctx->ctx.basectx)) + break; + if (!(stat.file_qid.type & LIB9P_QT_DIR) || !(stat.file_mode & LIB9P_DM_DIR)) { + lib9p_error(&ctx->ctx.basectx, + LINUX_ENOTDIR, "not a directory"); + break; + } + + memberp = dirp->vtable->dopen(&ctx->ctx, dirp, req->wname[resp->nwqid].utf8); + assert((memberp == NULL) == lib9p_ctx_has_error(&ctx->ctx.basectx)); + if (lib9p_ctx_has_error(&ctx->ctx.basectx)) + break; + } + + struct lib9p_stat stat = memberp->vtable->stat(&ctx->ctx, memberp); + if (lib9p_ctx_has_error(&ctx->ctx.basectx)) + break; + resp->wqid[resp->nwqid] = stat.file_qid; + + dirp->vtable->free(&ctx->ctx, dirp); + dirp = memberp; + } + if (resp->nwqid == req->nwname) { + if (req->newfid == req->fid) + *dirpp = dirp; + else + if (!fidmap_store(&ctx->parent_sess->fids, req->newfid, dirp)) { + lib9p_error(&ctx->ctx.basectx, + LINUX_EMFILE, "too many open files"); + dirp->vtable->free(&ctx->ctx, dirp); + } + } else if (resp->nwqid > 0) { + ctx->ctx.basectx.err_num = 0; + ctx->ctx.basectx.err_msg[0] = '\0'; + } } static void handle_Topen(struct _lib9p_srv_req *ctx, @@ -620,8 +682,7 @@ static void handle_Tclunk(struct _lib9p_srv_req *ctx, return; } - if ((*filepp)->vtable->free) - (*filepp)->vtable->free(&ctx->ctx, *filepp); + (*filepp)->vtable->free(&ctx->ctx, *filepp); fidmap_del(&ctx->parent_sess->fids, req->fid); } @@ -643,8 +704,14 @@ static void handle_Tstat(struct _lib9p_srv_req *ctx, assert(req); assert(resp); - lib9p_error(&ctx->ctx.basectx, - LINUX_EOPNOTSUPP, "stat not yet implemented"); + struct lib9p_srv_file **filepp = fidmap_load(&ctx->parent_sess->fids, req->fid); + if (!filepp) { + lib9p_errorf(&ctx->ctx.basectx, + LINUX_EBADF, "bad file number %"PRIu32, req->fid); + return; + } + + resp->stat = (*filepp)->vtable->stat(&ctx->ctx, *filepp); } static void handle_Twstat(struct _lib9p_srv_req *ctx, |