diff options
Diffstat (limited to 'lib9p/srv.c')
-rw-r--r-- | lib9p/srv.c | 144 |
1 files changed, 78 insertions, 66 deletions
diff --git a/lib9p/srv.c b/lib9p/srv.c index 0a33e03..7e2bab0 100644 --- a/lib9p/srv.c +++ b/lib9p/srv.c @@ -32,16 +32,6 @@ #include "config.h" -#ifndef CONFIG_9P_SRV_MAX_FIDS - #error config.h must define CONFIG_9P_SRV_MAX_FIDS -#endif -#ifndef CONFIG_9P_SRV_MAX_REQS - #error config.h must define CONFIG_9P_SRV_MAX_REQS -#endif -#ifndef CONFIG_9P_SRV_MAX_DEPTH - /* 1=just the root dir, 2=just files in the root dir, 3=1 subdir, ... */ - #error config.h must define CONFIG_9P_SRV_MAX_DEPTH -#endif #ifndef CONFIG_9P_SRV_MAX_MSG_SIZE #error config.h must define CONFIG_9P_SRV_MAX_MSG_SIZE #endif @@ -61,7 +51,7 @@ bool lib9p_srv_flush_requested(struct lib9p_srv_ctx *ctx) { void lib9p_srv_acknowledge_flush(struct lib9p_srv_ctx *ctx) { assert(ctx); assert(_lib9p_srv_flushch_can_send(&ctx->flushch)); - lib9p_error(&ctx->basectx, LINUX_ECANCELED, "request canceled by flush"); + lib9p_error(&ctx->basectx, LIB9P_ERRNO_L_ECANCELED, "request canceled by flush"); _lib9p_srv_flushch_send(&ctx->flushch, true); } @@ -80,6 +70,8 @@ typedef typeof( ((struct lib9p_qid){}).path ) srv_path_t; struct srv_pathinfo { lo_interface lib9p_srv_file file; enum srv_filetype type; + /* .parent_dir is used for (1) Twalk(".."), and (2) for checking + * permissions on the parent directory for remove(). */ srv_path_t parent_dir; /* References from other srv_pathinfos (via .parent_dir) or @@ -249,14 +241,18 @@ static inline struct srv_pathinfo *srv_path_save(struct srv_req *ctx, static inline void srv_path_decref(struct srv_req *ctx, srv_path_t path) { assert(ctx); - struct srv_pathinfo *pathinfo = map_load(&ctx->parent_sess->paths, path); - assert(pathinfo); - pathinfo->gc_refcount--; - if (pathinfo->gc_refcount == 0) { - if (pathinfo->parent_dir != path) - srv_path_decref(ctx, pathinfo->parent_dir); + for (;;) { + struct srv_pathinfo *pathinfo = map_load(&ctx->parent_sess->paths, path); + assert(pathinfo); + pathinfo->gc_refcount--; + if (pathinfo->gc_refcount) + break; + srv_path_t parent_path = pathinfo->parent_dir; LO_CALL(pathinfo->file, free); map_del(&ctx->parent_sess->paths, path); + if (parent_path == path) + break; + path = parent_path; } } @@ -271,7 +267,7 @@ static inline void srv_fid_del(struct srv_req *ctx, lib9p_fid_t fid, bool remove if (remove) { if (pathinfo->parent_dir == fidinfo->path) { lib9p_errorf(&ctx->basectx, - LINUX_EBUSY, "cannot remove root"); + LIB9P_ERRNO_L_EBUSY, "cannot remove root"); goto clunk; } struct srv_pathinfo *parent = map_load(&ctx->parent_sess->paths, pathinfo->parent_dir); @@ -279,7 +275,7 @@ static inline void srv_fid_del(struct srv_req *ctx, lib9p_fid_t fid, bool remove struct lib9p_stat parent_stat = LO_CALL(parent->file, stat, ctx); if (!srv_check_perm(ctx, &parent_stat, 0b010)) { lib9p_error(&ctx->basectx, - LINUX_EACCES, "remove: you do not have write permission on the parent directory"); + LIB9P_ERRNO_L_EACCES, "you do not have write permission on the parent directory"); goto clunk; } LO_CALL(pathinfo->file, remove, ctx); @@ -310,19 +306,29 @@ static inline void srv_fid_del(struct srv_req *ctx, lib9p_fid_t fid, bool remove * pathinfo->gc_refcount has already been incremented; does *not* * decrement it on failure. */ -static struct srv_fidinfo *srv_fid_store(struct srv_req *ctx, lib9p_fid_t fid, struct srv_pathinfo *pathinfo, bool overwrite) { +static inline struct srv_fidinfo *srv_fid_store(struct srv_req *ctx, lib9p_fid_t fid, struct srv_pathinfo *pathinfo, bool overwrite) { assert(ctx); assert(fid != LIB9P_FID_NOFID); assert(pathinfo); struct lib9p_qid qid = LO_CALL(pathinfo->file, qid); - if (map_load(&ctx->parent_sess->fids, fid)) { + struct srv_fidinfo *old_fidinfo = map_load(&ctx->parent_sess->fids, fid); + if (old_fidinfo) { if (overwrite) { - srv_fid_del(ctx, fid, false); + /* This should only happen from Twalk; because + * directories cannot be RCLOSE and Twalk cannot walk on + * FIDs open for I/O, we can skip most of + * srv_fid_del(). */ + assert(old_fidinfo->type == SRV_FILETYPE_DIR); + assert(old_fidinfo->flags == 0); + + old_fidinfo->authinfo = srv_authinfo_decref(old_fidinfo->authinfo); + srv_path_decref(ctx, old_fidinfo->path); + map_del(&ctx->parent_sess->fids, fid); } else { lib9p_error(&ctx->basectx, - LINUX_EBADF, "FID already in use"); + LIB9P_ERRNO_L_EBADF, "FID already in use"); return NULL; } } @@ -382,11 +388,11 @@ static void srv_respond_error(struct srv_req *req) { /* XXX: This assumes that a version's min_msg_size is the * Rerror overhead. That's true for the current - * implementation of protogen, but is a sneaky assumption. */ + * implementation of core_gen, but is a sneaky assumption. */ uint32_t overhead = lib9p_version_min_msg_size(sess->version); /* Truncate the error-string if necessary to avoid needing to - * return LINUX_ERANGE. */ + * return LIB9P_ERRNO_L_ERANGE. */ if (((uint32_t)host.errstr.len) + overhead > sess->max_msg_size) host.errstr.len = sess->max_msg_size - overhead; @@ -487,7 +493,7 @@ void lib9p_srv_read(struct lib9p_srv *srv, lo_interface net_stream_conn _conn) { }; if (goal > sess.max_msg_size) { lib9p_errorf(&req.basectx, - LINUX_EMSGSIZE, "T-message larger than %s limit (%zu > %"PRIu32")", + LIB9P_ERRNO_L_EMSGSIZE, "T-message larger than %s limit (%zu > %"PRIu32")", sess.initialized ? "negotiated" : "server", goal, sess.max_msg_size); @@ -712,7 +718,7 @@ static void handle_Tversion(struct srv_req *ctx, uint32_t min_msg_size = lib9p_version_min_msg_size(version); if (req->max_msg_size < min_msg_size) { lib9p_errorf(&ctx->basectx, - LINUX_EDOM, "requested max_msg_size is less than minimum for %s (%"PRIu32" < %"PRIu32")", + LIB9P_ERRNO_L_EDOM, "requested max_msg_size is less than minimum for %s (%"PRIu32" < %"PRIu32")", lib9p_version_str(version), req->max_msg_size, min_msg_size); return; } @@ -763,7 +769,7 @@ static void handle_Tauth(struct srv_req *ctx, struct lib9p_srv *srv = ctx->parent_sess->parent_conn->parent_srv; if (!srv->auth) { lib9p_error(&ctx->basectx, - LINUX_EOPNOTSUPP, "authentication not required"); + LIB9P_ERRNO_L_EOPNOTSUPP, "authentication not required"); return; } @@ -772,7 +778,7 @@ static void handle_Tauth(struct srv_req *ctx, srv->auth(ctx, req->aname); lib9p_error(&ctx->basectx, - LINUX_EOPNOTSUPP, "TODO: auth not implemented"); + LIB9P_ERRNO_L_EOPNOTSUPP, "TODO: auth not implemented"); if (lib9p_ctx_has_error(&ctx->basectx)) ctx->authinfo = srv_authinfo_decref(ctx->authinfo); @@ -785,7 +791,7 @@ static void handle_Tattach(struct srv_req *ctx, if (req->fid == LIB9P_FID_NOFID) { lib9p_error(&ctx->basectx, - LINUX_EBADF, "cannot assign to NOFID"); + LIB9P_ERRNO_L_EBADF, "cannot assign to NOFID"); return; } @@ -794,36 +800,36 @@ static void handle_Tattach(struct srv_req *ctx, struct srv_fidinfo *afid = map_load(&ctx->parent_sess->fids, req->afid); if (!afid) lib9p_error(&ctx->basectx, - LINUX_EACCES, "FID provided as auth-file is not a valid FID"); + LIB9P_ERRNO_L_EACCES, "FID provided as auth-file is not a valid FID"); else if (afid->type != SRV_FILETYPE_AUTH) lib9p_error(&ctx->basectx, - LINUX_EACCES, "FID provided as auth-file is not an auth-file"); + LIB9P_ERRNO_L_EACCES, "FID provided as auth-file is not an auth-file"); else if (!lib9p_str_eq(afid->authinfo->uname, req->uname)) lib9p_errorf(&ctx->basectx, - LINUX_EACCES, "FID provided as auth-file is for user=\"%.*s\" and cannot be used for user=\"%.*s\"", + LIB9P_ERRNO_L_EACCES, "FID provided as auth-file is for user=\"%.*s\" and cannot be used for user=\"%.*s\"", afid->authinfo->uname.len, afid->authinfo->uname.utf8, req->uname.len, req->uname.utf8); #if CONFIG_9P_ENABLE_9P2000_u else if (afid->authinfo->uid != req->n_uid) lib9p_errorf(&ctx->basectx, - LINUX_EACCES, "FID provided as auth-file is for user=%"PRIu32" and cannot be used for user=%"PRIu32, + LIB9P_ERRNO_L_EACCES, "FID provided as auth-file is for user=%"PRIu32" and cannot be used for user=%"PRIu32, afid->authinfo->uid, req->n_uid); #endif else if (!lib9p_str_eq(afid->auth.aname, req->aname)) lib9p_errorf(&ctx->basectx, - LINUX_EACCES, "FID provided as auth-file is for tree=\"%.*s\" and cannot be used for tree=\"%.*s\"", + LIB9P_ERRNO_L_EACCES, "FID provided as auth-file is for tree=\"%.*s\" and cannot be used for tree=\"%.*s\"", afid->auth.aname.len, afid->auth.aname.utf8, req->aname.len, req->aname.utf8); else if (!afid->auth.completed) lib9p_error(&ctx->basectx, - LINUX_EACCES, "FID provided as auth-file has not completed authentication"); + LIB9P_ERRNO_L_EACCES, "FID provided as auth-file has not completed authentication"); if (lib9p_ctx_has_error(&ctx->basectx)) return; ctx->authinfo = srv_authinfo_incref(afid->authinfo); } else { if (req->afid != LIB9P_FID_NOFID) { lib9p_error(&ctx->basectx, - LINUX_EACCES, "FID provided as auth-file, but no auth-file is required"); + LIB9P_ERRNO_L_EACCES, "FID provided as auth-file, but no auth-file is required"); return; } ctx->authinfo = srv_authinfo_new(req->uname, req->n_uid); @@ -872,14 +878,19 @@ static void handle_Twalk(struct srv_req *ctx, if (req->newfid == LIB9P_FID_NOFID) { lib9p_error(&ctx->basectx, - LINUX_EBADF, "cannot assign to NOFID"); + LIB9P_ERRNO_L_EBADF, "cannot assign to NOFID"); return; } struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, req->fid); if (!fidinfo) { lib9p_errorf(&ctx->basectx, - LINUX_EBADF, "bad file number %"PRIu32, req->fid); + LIB9P_ERRNO_L_EBADF, "bad file number %"PRIu32, req->fid); + return; + } + if (fidinfo->flags & FIDFLAG_OPEN) { + lib9p_error(&ctx->basectx, + LIB9P_ERRNO_L_EALREADY, "cannot walk on FID open for I/O"); return; } ctx->authinfo = srv_authinfo_incref(fidinfo->authinfo); @@ -890,23 +901,24 @@ static void handle_Twalk(struct srv_req *ctx, resp->wqid = (struct lib9p_qid *)(&resp[1]); for (resp->nwqid = 0; resp->nwqid < req->nwname; resp->nwqid++) { + if (pathinfo->type != SRV_FILETYPE_DIR) { + lib9p_error(&ctx->basectx, + LIB9P_ERRNO_L_ENOTDIR, "not a directory"); + break; + } + struct srv_pathinfo *new_pathinfo; if (lib9p_str_eq(req->wname[resp->nwqid], lib9p_str(".."))) { new_pathinfo = map_load(&ctx->parent_sess->paths, pathinfo->parent_dir); assert(new_pathinfo); new_pathinfo->gc_refcount++; } else { - if (pathinfo->type != SRV_FILETYPE_DIR) { - lib9p_error(&ctx->basectx, - LINUX_ENOTDIR, "not a directory"); - break; - } - lo_interface lib9p_srv_file member_file = LO_CALL(pathinfo->file, dwalk, ctx, req->wname[resp->nwqid]); assert(LO_IS_NULL(member_file) == lib9p_ctx_has_error(&ctx->basectx)); if (lib9p_ctx_has_error(&ctx->basectx)) break; new_pathinfo = srv_path_save(ctx, member_file, LO_CALL(pathinfo->file, qid).path); + assert(new_pathinfo); } if (new_pathinfo->type == SRV_FILETYPE_DIR) { @@ -916,7 +928,7 @@ static void handle_Twalk(struct srv_req *ctx, lib9p_stat_assert(stat); if (!srv_check_perm(ctx, &stat, 0b001)) { lib9p_error(&ctx->basectx, - LINUX_EACCES, "you do not have execute permission on that directory"); + LIB9P_ERRNO_L_EACCES, "you do not have execute permission on that directory"); srv_path_decref(ctx, LO_CALL(new_pathinfo->file, qid).path); break; } @@ -948,12 +960,12 @@ static void handle_Topen(struct srv_req *ctx, struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, req->fid); if (!fidinfo) { lib9p_errorf(&ctx->basectx, - LINUX_EBADF, "bad file number %"PRIu32, req->fid); + LIB9P_ERRNO_L_EBADF, "bad file number %"PRIu32, req->fid); return; } if (fidinfo->flags & FIDFLAG_OPEN) { lib9p_error(&ctx->basectx, - LINUX_EALREADY, "FID is already open"); + LIB9P_ERRNO_L_EALREADY, "FID is already open"); return; } if (fidinfo->type == SRV_FILETYPE_DIR) { @@ -961,7 +973,7 @@ static void handle_Topen(struct srv_req *ctx, (req->mode & LIB9P_O_TRUNC) || (req->mode & LIB9P_O_RCLOSE) ) { lib9p_error(&ctx->basectx, - LINUX_EISDIR, "directories cannot be written, executed, truncated, or removed-on-close"); + LIB9P_ERRNO_L_EISDIR, "directories cannot be written, executed, truncated, or removed-on-close"); return; } } @@ -983,7 +995,7 @@ static void handle_Topen(struct srv_req *ctx, lib9p_stat_assert(parent_stat); if (!srv_check_perm(ctx, &parent_stat, 0b010)) { lib9p_error(&ctx->basectx, - LINUX_EACCES, "permission denied to remove-on-close"); + LIB9P_ERRNO_L_EACCES, "permission denied to remove-on-close"); goto topen_return; } fidflags |= FIDFLAG_RCLOSE; @@ -994,7 +1006,7 @@ static void handle_Topen(struct srv_req *ctx, lib9p_stat_assert(stat); if ((stat.file_mode & LIB9P_DM_EXCL) && pathinfo->io_refcount) { lib9p_error(&ctx->basectx, - LINUX_EEXIST, "exclusive file is already opened"); + LIB9P_ERRNO_L_EEXIST, "exclusive file is already opened"); goto topen_return; } if (stat.file_mode & LIB9P_DM_APPEND) @@ -1021,7 +1033,7 @@ static void handle_Topen(struct srv_req *ctx, } if (!srv_check_perm(ctx, &stat, perm_bits)) { lib9p_error(&ctx->basectx, - LINUX_EACCES, "permission denied"); + LIB9P_ERRNO_L_EACCES, "permission denied"); goto topen_return; } @@ -1076,7 +1088,7 @@ static void handle_Tcreate(struct srv_req *ctx, srv_handler_common(ctx, req, resp); lib9p_error(&ctx->basectx, - LINUX_EOPNOTSUPP, "create not (yet?) implemented"); + LIB9P_ERRNO_L_EOPNOTSUPP, "create not (yet?) implemented"); } static void handle_Tread(struct srv_req *ctx, @@ -1090,12 +1102,12 @@ static void handle_Tread(struct srv_req *ctx, struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, req->fid); if (!fidinfo) { lib9p_errorf(&ctx->basectx, - LINUX_EBADF, "bad file number %"PRIu32, req->fid); + LIB9P_ERRNO_L_EBADF, "bad file number %"PRIu32, req->fid); return; } if (!(fidinfo->flags & FIDFLAG_OPEN_R)) { lib9p_error(&ctx->basectx, - LINUX_EINVAL, "FID not open for reading"); + LIB9P_ERRNO_L_EINVAL, "FID not open for reading"); return; } @@ -1111,7 +1123,7 @@ static void handle_Tread(struct srv_req *ctx, idx = fidinfo->dir.idx; else { lib9p_errorf(&ctx->basectx, - LINUX_EINVAL, "invalid offset (must be 0 or %"PRIu64"): %"PRIu64, + LIB9P_ERRNO_L_EINVAL, "invalid offset (must be 0 or %"PRIu64"): %"PRIu64, fidinfo->dir.off, req->offset); ctx->authinfo = srv_authinfo_decref(ctx->authinfo); return; @@ -1159,12 +1171,12 @@ static void handle_Twrite(struct srv_req *ctx, struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, req->fid); if (!fidinfo) { lib9p_errorf(&ctx->basectx, - LINUX_EBADF, "bad file number %"PRIu32, req->fid); + LIB9P_ERRNO_L_EBADF, "bad file number %"PRIu32, req->fid); return; } if (!(fidinfo->flags & FIDFLAG_OPEN_W)) { lib9p_error(&ctx->basectx, - LINUX_EINVAL, "FID not open for writing"); + LIB9P_ERRNO_L_EINVAL, "FID not open for writing"); return; } @@ -1182,7 +1194,7 @@ static void handle_Tclunk(struct srv_req *ctx, struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, req->fid); if (!fidinfo) { lib9p_errorf(&ctx->basectx, - LINUX_EBADF, "bad file number %"PRIu32, req->fid); + LIB9P_ERRNO_L_EBADF, "bad file number %"PRIu32, req->fid); return; } @@ -1199,7 +1211,7 @@ static void handle_Tremove(struct srv_req *ctx, struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, req->fid); if (!fidinfo) { lib9p_errorf(&ctx->basectx, - LINUX_EBADF, "bad file number %"PRIu32, req->fid); + LIB9P_ERRNO_L_EBADF, "bad file number %"PRIu32, req->fid); return; } @@ -1216,7 +1228,7 @@ static void handle_Tstat(struct srv_req *ctx, struct srv_fidinfo *fidinfo = map_load(&ctx->parent_sess->fids, req->fid); if (!fidinfo) { lib9p_errorf(&ctx->basectx, - LINUX_EBADF, "bad file number %"PRIu32, req->fid); + LIB9P_ERRNO_L_EBADF, "bad file number %"PRIu32, req->fid); return; } struct srv_pathinfo *pathinfo = map_load(&ctx->parent_sess->paths, fidinfo->path); @@ -1235,7 +1247,7 @@ static void handle_Twstat(struct srv_req *ctx, srv_handler_common(ctx, req, resp); lib9p_error(&ctx->basectx, - LINUX_EOPNOTSUPP, "wstat not (yet?) implemented"); + LIB9P_ERRNO_L_EOPNOTSUPP, "wstat not (yet?) implemented"); } #if CONFIG_9P_ENABLE_9P2000_p9p @@ -1245,7 +1257,7 @@ static void handle_Topenfd(struct srv_req *ctx, srv_handler_common(ctx, req, resp); lib9p_error(&ctx->basectx, - LINUX_EOPNOTSUPP, "openfd not (yet?) implemented"); + LIB9P_ERRNO_L_EOPNOTSUPP, "openfd not (yet?) implemented"); } #endif @@ -1256,7 +1268,7 @@ static void handle_Tsession(struct srv_req *ctx, srv_handler_common(ctx, req, resp); lib9p_error(&ctx->basectx, - LINUX_EOPNOTSUPP, "session not (yet?) implemented"); + LIB9P_ERRNO_L_EOPNOTSUPP, "session not (yet?) implemented"); } static void handle_Tsread(struct srv_req *ctx, @@ -1265,7 +1277,7 @@ static void handle_Tsread(struct srv_req *ctx, srv_handler_common(ctx, req, resp); lib9p_error(&ctx->basectx, - LINUX_EOPNOTSUPP, "sread not (yet?) implemented"); + LIB9P_ERRNO_L_EOPNOTSUPP, "sread not (yet?) implemented"); } static void handle_Tswrite(struct srv_req *ctx, @@ -1274,6 +1286,6 @@ static void handle_Tswrite(struct srv_req *ctx, srv_handler_common(ctx, req, resp); lib9p_error(&ctx->basectx, - LINUX_EOPNOTSUPP, "swrite not (yet?) implemented"); + LIB9P_ERRNO_L_EOPNOTSUPP, "swrite not (yet?) implemented"); } #endif |