2021-06-13 17:22:58 +00:00
|
|
|
#include <u.h>
|
|
|
|
#include <libc.h>
|
|
|
|
#include <fcall.h>
|
|
|
|
#include <thread.h>
|
|
|
|
#include <9p.h>
|
|
|
|
#include <stdio.h>
|
2021-06-24 07:17:34 +00:00
|
|
|
#include <dirent.h>
|
|
|
|
#include <libString.h>
|
2021-06-28 21:37:59 +00:00
|
|
|
#include <mp.h>
|
|
|
|
#include <libsec.h>
|
2021-06-24 07:17:34 +00:00
|
|
|
|
2021-06-13 17:22:58 +00:00
|
|
|
/* clang-format off */
|
|
|
|
|
|
|
|
char clca(char c) { return c >= 'A' && c <= 'Z' ? c + ('a' - 'A') : c; } /* char to lowercase */
|
|
|
|
char cuca(char c) { return c >= 'a' && c <= 'z' ? c - ('a' - 'A') : c; } /* char to uppercase */
|
|
|
|
int slen(char *s) { int i = 0; while(s[i] && s[++i]) { ; } return i; } /* string length */
|
|
|
|
char *st__(char *s, char (*fn)(char)) { int i = 0; char c; while((c = s[i])) s[i++] = fn(c); return s; }
|
|
|
|
char *stuc(char *s) { return st__(s, cuca); } /* string to uppercase */
|
|
|
|
char *stlc(char *s) { return st__(s, clca); } /* string to lowercase */
|
|
|
|
char *scpy(char *src, char *dst, int len) { int i = 0; while((dst[i] = src[i]) && i < len - 2) i++; dst[i + 1] = '\0'; return dst; } /* string copy */
|
|
|
|
int scmp(char *a, char *b) { int i = 0; while(a[i] == b[i]) if(!a[i++]) return 1; return 0; } /* string compare */
|
|
|
|
char *scsw(char *s, char a, char b) { int i = 0; char c; while((c = s[i])) s[i++] = c == a ? b : c; return s; } /* string char swap */
|
|
|
|
char *scat(char *dst, const char *src) { char *ptr = dst + slen(dst); while(*src) *ptr++ = *src++; *ptr = '\0'; return dst; } /* string cat */
|
|
|
|
int ssin(char *s, char *ss) { int a = 0, b = 0; while(s[a]) { if(s[a] == ss[b]) { if(!ss[b + 1]) return a - b; b++; } else b = 0; a++; } return -1; } /* string substring index */
|
|
|
|
char *ccat(char *dst, char c) { int len = slen(dst); dst[len] = c; dst[len + 1] = '\0'; return dst; }
|
|
|
|
|
|
|
|
/* clang-format on */
|
2021-06-28 21:37:59 +00:00
|
|
|
int chatty9p = 1;
|
2021-06-13 17:22:58 +00:00
|
|
|
|
2021-06-16 05:46:47 +00:00
|
|
|
static char Ebad[] = "something bad happened";
|
|
|
|
static char Enomem[] = "no memory";
|
2021-07-01 07:45:48 +00:00
|
|
|
static char Euname[] = "username is already taken";
|
2021-06-16 05:46:47 +00:00
|
|
|
|
2021-07-02 01:59:56 +00:00
|
|
|
static Tree* tree;
|
|
|
|
|
2021-07-02 21:59:40 +00:00
|
|
|
typedef enum {
|
|
|
|
LOGIN = 208176873,
|
|
|
|
LOAD = 5626172,
|
|
|
|
CREATE = 7083959236,
|
|
|
|
PROTECT = 295480618573,
|
|
|
|
ENTER = 195024746,
|
|
|
|
LEAVE = 207662601,
|
|
|
|
LOGOUT = 7702552890,
|
|
|
|
SAVE = 5962355,
|
|
|
|
RESET = 218931595,
|
|
|
|
UNLOAD = 8325026851
|
|
|
|
} Command;
|
|
|
|
|
2021-06-30 06:52:57 +00:00
|
|
|
typedef enum { CTL = 1, USERS, CARTS, SLOT, DATA, REALMS, UNIVERSE } FileType;
|
2021-06-29 18:56:21 +00:00
|
|
|
|
|
|
|
typedef struct Aux Aux;
|
|
|
|
struct Aux {
|
2021-06-30 06:52:57 +00:00
|
|
|
FileType type;
|
2021-06-16 05:46:47 +00:00
|
|
|
char* data;
|
2021-06-29 18:56:21 +00:00
|
|
|
int count;
|
2021-06-16 05:46:47 +00:00
|
|
|
};
|
2021-06-14 20:23:06 +00:00
|
|
|
|
2021-06-30 06:52:57 +00:00
|
|
|
Aux* create_aux(FileType t) {
|
|
|
|
Aux* self = (Aux*)malloc(sizeof(Aux));
|
2021-07-01 07:45:48 +00:00
|
|
|
self->type = t;
|
|
|
|
self->data = nil;
|
|
|
|
self->count = 0;
|
|
|
|
|
2021-06-30 06:52:57 +00:00
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2021-07-01 07:45:48 +00:00
|
|
|
typedef struct UserInfo UserInfo;
|
|
|
|
struct UserInfo {
|
|
|
|
char name[32];
|
2021-07-02 21:59:40 +00:00
|
|
|
uvlong password;
|
2021-07-03 04:32:10 +00:00
|
|
|
char realm[32];
|
|
|
|
char cart[32];
|
2021-07-01 07:45:48 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static UserInfo users_table[64];
|
|
|
|
|
2021-07-02 21:59:40 +00:00
|
|
|
uvlong hash(char* str) {
|
|
|
|
uvlong h;
|
|
|
|
uchar* p;
|
|
|
|
|
|
|
|
h = 0;
|
|
|
|
for (p = (unsigned char*)str; *p != '\0'; p++)
|
|
|
|
h = 37 * h + *p;
|
|
|
|
return h; // or, h % ARRAY_SIZE;
|
|
|
|
}
|
|
|
|
|
2021-07-01 07:45:48 +00:00
|
|
|
void xrxs_attach(Req* r) {
|
2021-07-02 01:59:56 +00:00
|
|
|
/* As it is, once the user detaches, they will stay in the table
|
|
|
|
* until the server restarts. We have to figure out some way
|
|
|
|
* to detect a detach to remove them... You can't delay respond()
|
|
|
|
* or the attach will never complete.
|
|
|
|
*/
|
2021-07-01 07:45:48 +00:00
|
|
|
int i = 0;
|
|
|
|
int l = 0;
|
|
|
|
char* usr;
|
|
|
|
char* username = r->ifcall.uname;
|
|
|
|
usr = username;
|
|
|
|
while (*usr) {
|
|
|
|
l++;
|
|
|
|
usr++;
|
|
|
|
}
|
|
|
|
for (i = 0; i < 64; i++) {
|
|
|
|
usr = users_table[i].name;
|
2021-07-02 21:59:40 +00:00
|
|
|
if (scmp(usr, username)) {
|
2021-07-01 07:45:48 +00:00
|
|
|
respond(r, Euname);
|
|
|
|
return;
|
2021-07-02 21:59:40 +00:00
|
|
|
}
|
2021-07-01 07:45:48 +00:00
|
|
|
if (*usr == 0) {
|
2021-07-02 21:59:40 +00:00
|
|
|
scpy(username, usr, l + 1);
|
2021-07-01 07:45:48 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
respond(r, nil);
|
|
|
|
}
|
|
|
|
|
2021-07-02 21:59:40 +00:00
|
|
|
void login(char* uname, char* password) {
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < 64; i++) {
|
|
|
|
if (scmp(uname, users_table[i].name)) {
|
|
|
|
users_table[i].password = hash(password);
|
|
|
|
}
|
2021-06-16 05:46:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-02 21:59:40 +00:00
|
|
|
int logout(char* uname) {
|
|
|
|
int i;
|
|
|
|
fprintf(stderr, uname);
|
|
|
|
for (i = 0; i < 64; i++) {
|
|
|
|
if (scmp(uname, users_table[i].name)) {
|
|
|
|
*(users_table[i].name) = 0;
|
|
|
|
users_table[i].password = 0;
|
2021-07-03 04:32:10 +00:00
|
|
|
*(users_table[i].realm) = 0;
|
2021-07-02 21:59:40 +00:00
|
|
|
return 1;
|
2021-06-16 05:46:47 +00:00
|
|
|
}
|
|
|
|
}
|
2021-07-02 21:59:40 +00:00
|
|
|
return 0;
|
2021-06-13 17:22:58 +00:00
|
|
|
}
|
|
|
|
|
2021-07-03 04:32:10 +00:00
|
|
|
int load(char* cart) {
|
|
|
|
/* 1. get file handle to cartridge file
|
|
|
|
* 2. make cartridge file available in CART.data for this user
|
|
|
|
* 3. add cartridge name to user's UserInfo
|
|
|
|
* 4. if cartridge data dir is not empty, walk it
|
|
|
|
* 5. for each file in data dir, create corresponding
|
|
|
|
* data file in DATA/ for this user, grab a file handle, and make
|
|
|
|
* the data available in DATA.data
|
|
|
|
*/
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int rcreate(char* realm) { // create is taken in the libc header!
|
|
|
|
/* 1. split input by space -- if 2+ elements, second element
|
|
|
|
* is the max number of members (default 4)
|
|
|
|
* 2. check if realm exists; return 0 if it does
|
|
|
|
* 3. create real files in the realms directory and fill as
|
|
|
|
* appropriate
|
|
|
|
*/
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int protect(char* password) {
|
|
|
|
/* 1. if current realm has a password already, return 0;
|
|
|
|
* 2. otherwise, hash the password and put it after the
|
|
|
|
* member limit in the realm file
|
|
|
|
*/
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int enter(char* realm) {
|
|
|
|
/* 1. get password for realm; if none, skip to 3
|
|
|
|
* 2. check password for current user against it;
|
|
|
|
* return 0 if different
|
|
|
|
* 3. if member limit already met, return 0
|
|
|
|
* 4. otherwise, insert username in the realm
|
|
|
|
* and the realm name in the user's UserInfo
|
|
|
|
*/
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int leave(char* uname) {
|
|
|
|
/* 1. if not in a realm, return 0;
|
|
|
|
* 2. else remove self from realm file
|
|
|
|
* and remove realm from user's UserInfo
|
|
|
|
*/
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int save(char* uname) {
|
|
|
|
/* 1. flush this user's universe to the realm;
|
|
|
|
* maybe this is not needed
|
|
|
|
*/
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int reset(char* uname) {
|
|
|
|
/* 1. save
|
|
|
|
* 2. leave
|
|
|
|
* 3. clear this user's password
|
|
|
|
* 4. the client should now be able to restart execution
|
|
|
|
* of the cartridge safely
|
|
|
|
*/
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int unload(char* uname) {
|
|
|
|
/* 1. reset
|
|
|
|
* 2. clear cartridge data from CART->data for this user
|
|
|
|
* 3. destroy all files in DATA/ for this user
|
|
|
|
* 4. remove cartridge from UserInfo for thi user
|
|
|
|
* 5. the client should now be able to unload
|
|
|
|
* the cartridge from memory safely and restart
|
|
|
|
* the 'firmware' ROM execution
|
|
|
|
*/
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-06-30 06:52:57 +00:00
|
|
|
void write_ctl(Req* r) {
|
2021-07-02 21:59:40 +00:00
|
|
|
char cmd[16] = {0};
|
2021-06-28 21:37:59 +00:00
|
|
|
char* c = r->ifcall.data;
|
2021-06-29 18:56:21 +00:00
|
|
|
int i;
|
2021-06-28 21:37:59 +00:00
|
|
|
|
2021-07-02 21:59:40 +00:00
|
|
|
for (i = 0; i < r->ifcall.count && *c != ' ' && *c != '\n'; i++) {
|
2021-06-29 18:56:21 +00:00
|
|
|
ccat(cmd, *c++);
|
2021-06-28 21:37:59 +00:00
|
|
|
}
|
2021-07-02 21:59:40 +00:00
|
|
|
fprintf(stderr, cmd);
|
|
|
|
uvlong const cmd_hashv = hash(cmd);
|
2021-06-28 21:37:59 +00:00
|
|
|
switch (cmd_hashv) {
|
2021-07-02 21:59:40 +00:00
|
|
|
case LOGIN:
|
|
|
|
login(r->fid->uid, c);
|
|
|
|
break;
|
|
|
|
case LOAD:
|
|
|
|
// load(c);
|
|
|
|
break;
|
|
|
|
case CREATE:
|
2021-07-03 04:32:10 +00:00
|
|
|
// rcreate(c);
|
2021-07-02 21:59:40 +00:00
|
|
|
break;
|
|
|
|
case PROTECT:
|
|
|
|
// protect(c);
|
|
|
|
break;
|
|
|
|
case ENTER:
|
|
|
|
// enter(c);
|
|
|
|
break;
|
|
|
|
case LEAVE:
|
2021-07-03 04:32:10 +00:00
|
|
|
// leave(r->fid->uid);
|
2021-07-02 21:59:40 +00:00
|
|
|
break;
|
|
|
|
case LOGOUT:
|
|
|
|
logout(r->fid->uid);
|
|
|
|
break;
|
|
|
|
case SAVE:
|
2021-07-03 04:32:10 +00:00
|
|
|
// save(r->fid->uid);
|
2021-07-02 21:59:40 +00:00
|
|
|
break;
|
|
|
|
case RESET:
|
2021-07-03 04:32:10 +00:00
|
|
|
// reset(r->fid->uid);
|
2021-07-02 21:59:40 +00:00
|
|
|
break;
|
|
|
|
case UNLOAD:
|
2021-07-03 04:32:10 +00:00
|
|
|
// unload(r->fid->uid);
|
2021-07-02 21:59:40 +00:00
|
|
|
break;
|
|
|
|
}
|
2021-06-29 18:56:21 +00:00
|
|
|
r->ofcall.count = r->ifcall.count;
|
|
|
|
r->fid->file->dir.length = r->ifcall.count;
|
2021-06-30 06:52:57 +00:00
|
|
|
respond(r, nil);
|
2021-06-28 21:37:59 +00:00
|
|
|
}
|
|
|
|
|
2021-06-30 06:52:57 +00:00
|
|
|
void xrxs_write(Req* r) {
|
2021-06-29 18:56:21 +00:00
|
|
|
Aux* a = r->fid->file->aux;
|
|
|
|
switch (a->type) {
|
|
|
|
case CTL:
|
2021-06-30 06:52:57 +00:00
|
|
|
write_ctl(r);
|
|
|
|
break;
|
|
|
|
case UNIVERSE:
|
2021-07-01 07:45:48 +00:00
|
|
|
// write_universe(r);
|
2021-06-28 21:37:59 +00:00
|
|
|
break;
|
|
|
|
default:
|
2021-06-30 06:52:57 +00:00
|
|
|
respond(r, nil);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-01 07:45:48 +00:00
|
|
|
void read_users(Req* r) {
|
2021-07-02 21:59:40 +00:00
|
|
|
char buf[2113] = {0};
|
2021-07-01 07:45:48 +00:00
|
|
|
int i;
|
|
|
|
for (i = 0; i < 64; i++) {
|
2021-07-02 21:59:40 +00:00
|
|
|
if (scmp(users_table[i].name, r->fid->uid)) {
|
|
|
|
scat(buf, users_table[i].name);
|
|
|
|
ccat(buf, '\n');
|
2021-07-01 07:45:48 +00:00
|
|
|
break;
|
|
|
|
}
|
2021-07-02 21:59:40 +00:00
|
|
|
}
|
|
|
|
for (i = 0; i < 64; i++) {
|
|
|
|
if (
|
|
|
|
scmp(users_table[i].name, "\0") ||
|
|
|
|
scmp(users_table[i].name, r->fid->uid)) {
|
|
|
|
continue;
|
2021-07-01 07:45:48 +00:00
|
|
|
}
|
2021-07-02 21:59:40 +00:00
|
|
|
scat(buf, users_table[i].name);
|
|
|
|
ccat(buf, '\n');
|
2021-07-01 07:45:48 +00:00
|
|
|
}
|
2021-07-02 21:59:40 +00:00
|
|
|
ccat(buf, 0);
|
2021-07-01 07:45:48 +00:00
|
|
|
readstr(r, buf);
|
|
|
|
respond(r, nil);
|
|
|
|
}
|
|
|
|
|
2021-06-30 06:52:57 +00:00
|
|
|
void xrxs_read(Req* r) {
|
|
|
|
Aux* a = r->fid->file->aux;
|
|
|
|
switch (a->type) {
|
|
|
|
case USERS:
|
2021-07-01 07:45:48 +00:00
|
|
|
read_users(r);
|
2021-06-30 06:52:57 +00:00
|
|
|
break;
|
|
|
|
case CARTS:
|
2021-07-01 07:45:48 +00:00
|
|
|
// read_carts(r);
|
2021-06-30 06:52:57 +00:00
|
|
|
break;
|
|
|
|
case SLOT:
|
2021-07-01 07:45:48 +00:00
|
|
|
// read_slot(r);
|
2021-06-30 06:52:57 +00:00
|
|
|
break;
|
|
|
|
case DATA:
|
2021-07-01 07:45:48 +00:00
|
|
|
// read_data(r);
|
2021-06-30 06:52:57 +00:00
|
|
|
break;
|
|
|
|
case REALMS:
|
2021-07-01 07:45:48 +00:00
|
|
|
// read_realms(r);
|
2021-06-30 06:52:57 +00:00
|
|
|
break;
|
|
|
|
case UNIVERSE:
|
2021-07-01 07:45:48 +00:00
|
|
|
// read_universe(r);
|
2021-06-30 06:52:57 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
respond(r, nil);
|
2021-06-28 21:37:59 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-30 06:52:57 +00:00
|
|
|
void fs_destroy_file(File* f) {
|
2021-06-29 18:56:21 +00:00
|
|
|
Aux* a = f->aux;
|
|
|
|
if (a && a->data) {
|
|
|
|
free(a->data);
|
|
|
|
free(a);
|
|
|
|
} else if (a) {
|
|
|
|
free(a);
|
2021-06-16 05:46:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-24 07:17:34 +00:00
|
|
|
String** listdir(char* path) {
|
|
|
|
String** self = malloc(128 * sizeof(String*));
|
|
|
|
DIR* dir;
|
|
|
|
struct dirent* ent;
|
|
|
|
int i = 0;
|
|
|
|
char* c;
|
|
|
|
if ((dir = opendir(path)) != NULL) {
|
|
|
|
while ((ent = readdir(dir)) != NULL) {
|
|
|
|
c = ent->d_name;
|
|
|
|
if (scmp(c, ".") || scmp(c, "..")) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
self[i] = s_new();
|
|
|
|
|
|
|
|
while (*c) {
|
|
|
|
s_putc(self[i], *c++);
|
|
|
|
}
|
|
|
|
s_terminate(self[i++]);
|
|
|
|
}
|
|
|
|
closedir(dir);
|
|
|
|
}
|
|
|
|
self[i] = nil;
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2021-07-02 21:59:40 +00:00
|
|
|
Srv fs = {.attach = xrxs_attach, .read = xrxs_read, .write = xrxs_write};
|
2021-06-13 17:22:58 +00:00
|
|
|
|
2021-06-23 06:48:23 +00:00
|
|
|
int threadmaybackground(void) { return 1; }
|
2021-06-16 05:46:47 +00:00
|
|
|
|
2021-06-14 20:23:06 +00:00
|
|
|
void threadmain(int argc, char* argv[]) {
|
|
|
|
char* mtpt = nil;
|
|
|
|
char* usocket = nil;
|
|
|
|
int i;
|
2021-06-24 07:17:34 +00:00
|
|
|
String** cart;
|
2021-06-14 20:23:06 +00:00
|
|
|
|
2021-07-02 21:59:40 +00:00
|
|
|
/* if -h CMD is given, print the hash value of CMD */
|
|
|
|
if (argc == 3 && scmp(argv[1], "-h")) {
|
|
|
|
printf("%llu\n", hash(argv[2]));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-06-14 20:23:06 +00:00
|
|
|
/* if -m PATH is supplied, mount on PATH */
|
|
|
|
/* if -s NAME is supplied, create a socket for the namespace */
|
|
|
|
/* otherwise, just use srv() (for wrapping with socat or inetd) */
|
2021-06-13 17:22:58 +00:00
|
|
|
|
2021-06-14 20:23:06 +00:00
|
|
|
if (argc >= 3) {
|
|
|
|
for (i = 0; i < argc; i++) {
|
|
|
|
if (scmp(argv[i], "-m")) {
|
|
|
|
mtpt = argv[++i];
|
|
|
|
printf("serving on %s", mtpt);
|
|
|
|
} else if (scmp(argv[i], "-s")) {
|
|
|
|
usocket = argv[++i];
|
|
|
|
printf("serving socket namespace %s", usocket);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-29 18:56:21 +00:00
|
|
|
fs.foreground = 1;
|
2021-06-30 06:52:57 +00:00
|
|
|
fs.tree = alloctree(nil, nil, DMDIR | 0777, fs_destroy_file);
|
2021-06-23 06:48:23 +00:00
|
|
|
tree = fs.tree;
|
2021-07-02 21:59:40 +00:00
|
|
|
|
2021-07-01 07:45:48 +00:00
|
|
|
closefile(
|
|
|
|
createfile(tree->root, "ctl", nil, DMAPPEND | 0300, create_aux(CTL)));
|
2021-07-02 21:59:40 +00:00
|
|
|
closefile(createfile(tree->root, "carts", nil, 0400, create_aux(CARTS)));
|
|
|
|
closefile(createfile(tree->root, "users", nil, 0400, create_aux(USERS)));
|
|
|
|
closefile(createfile(tree->root, "slot", nil, 0400, create_aux(SLOT)));
|
|
|
|
closefile(createfile(tree->root, "data", nil, DMDIR | 0500, nil));
|
|
|
|
closefile(createfile(tree->root, "realms", nil, 0400, create_aux(REALMS)));
|
|
|
|
closefile(
|
|
|
|
createfile(tree->root, "universe", nil, 0600, create_aux(UNIVERSE)));
|
2021-07-01 07:45:48 +00:00
|
|
|
/*String** carts = listdir("carts/");
|
2021-06-24 07:17:34 +00:00
|
|
|
cart = carts;
|
2021-06-13 17:22:58 +00:00
|
|
|
|
2021-07-01 07:45:48 +00:00
|
|
|
while (*cart) {
|
|
|
|
// just concatenate the carts into a multiline string, and put it in
|
|
|
|
CARTS.data
|
|
|
|
}*/
|
2021-06-30 06:52:57 +00:00
|
|
|
|
2021-07-01 07:45:48 +00:00
|
|
|
if (argc >= 3) {
|
|
|
|
if (mtpt != nil && access(mtpt, AEXIST) < 0 && access(mtpt, AEXIST) < 0)
|
|
|
|
sysfatal("mountpoint %s does not exist", mtpt);
|
2021-06-13 17:22:58 +00:00
|
|
|
|
2021-07-01 07:45:48 +00:00
|
|
|
threadpostmountsrv(&fs, usocket, mtpt, MREPL | MCREATE);
|
|
|
|
threadexits(0);
|
|
|
|
} else {
|
|
|
|
srv(&fs);
|
|
|
|
}
|
2021-06-14 20:23:06 +00:00
|
|
|
}
|