13 /* telnet control character */
16 /* representation types */
20 /* transmission modes */
30 /* read/write buffer size */
33 /* maximum ms we'll wait for a command */
34 Maxwait= 1000*60*30, /* inactive for 30 minutes, we hang up */
50 int namelistcmd(char*);
60 int reply(char*, ...);
61 int restartcmd(char*);
62 int retrievecmd(char*);
74 int crlfwrite(int, char*, int);
78 typedef struct Cmd Cmd;
88 { "abor", abortcmd, 0, },
89 { "appe", appendcmd, 1, },
90 { "cdup", cdupcmd, 1, },
91 { "cwd", cwdcmd, 1, },
92 { "dele", delcmd, 1, },
93 { "help", helpcmd, 0, },
94 { "list", listcmd, 1, },
95 { "mdtm", mdtmcmd, 1, },
96 { "mkd", mkdircmd, 1, },
97 { "mode", modecmd, 0, },
98 { "nlst", namelistcmd, 1, },
99 { "noop", nopcmd, 0, },
100 { "opts", optscmd, 0, },
101 { "pass", passcmd, 0, },
102 { "pasv", pasvcmd, 1, },
103 { "pwd", pwdcmd, 0, },
104 { "port", portcmd, 1, },
105 { "quit", quitcmd, 0, },
106 { "rest", restartcmd, 1, },
107 { "retr", retrievecmd, 1, },
108 { "rmd", delcmd, 1, },
109 { "rnfr", rnfrcmd, 1, },
110 { "rnto", rntocmd, 1, },
111 { "site", sitecmd, 1, },
112 { "size", sizecmd, 1, },
113 { "stor", storecmd, 1, },
114 { "stou", storeucmd, 1, },
115 { "stru", structcmd, 1, },
116 { "syst", systemcmd, 0, },
117 { "type", typecmd, 0, },
118 { "user", usercmd, 0, },
122 #define NONENS "/lib/namespace.ftp" /* default ns for none */
124 char user[Maxpath]; /* logged in user */
125 char curdir[Maxpath]; /* current directory path */
128 int type; /* transmission type */
129 int mode; /* transmission mode */
130 int structure; /* file structure */
131 char data[64]; /* data address */
132 int pid; /* transfer process */
133 int encryption; /* encryption state */
134 int isnone, anon_ok, anon_only, anon_everybody;
135 char cputype[Maxpath]; /* the environment variable of the same name */
136 char bindir[Maxpath]; /* bin directory for this architecture */
137 char mailaddr[Maxpath];
138 char *namespace = NONENS;
141 int createperm = 0660;
143 vlong offset; /* from restart command */
147 typedef struct Passive Passive;
154 uchar ipaddr[IPaddrlen];
160 logit(char *fmt, ...)
166 rerrstr(errstr, sizeof errstr);
168 vseprint(buf, buf+sizeof(buf), fmt, arg);
170 syslog(0, FTPLOG, "%s.%s %s", nci->rsys, nci->rserv, buf);
171 werrstr(errstr, sizeof errstr);
177 syslog(0, "ftp", "usage: %s [-aAde] [-n nsfile]", argv0);
178 fprint(2, "usage: %s [-aAde] [-n nsfile]\n", argv0);
183 * read commands from the control stream and dispatch
186 main(int argc, char **argv)
196 case 'a': /* anonymous OK */
211 namespace = EARGF(usage());
217 /* open log file before doing a newns */
218 syslog(0, FTPLOG, nil);
220 /* find out who is calling */
222 nci = getnetconninfo(nil, 0);
224 nci = getnetconninfo(argv[argc-1], 0);
226 sysfatal("ftpd needs a network address");
228 strcpy(mailaddr, "?");
231 /* figure out which binaries to bind in later (only for none) */
232 arg = getenv("cputype");
234 strecpy(cputype, cputype+sizeof cputype, arg);
236 strcpy(cputype, "mips");
237 /* shurely /%s/bin */
238 snprint(bindir, sizeof(bindir), "/bin/%s/bin", cputype);
240 Binit(&in, 0, OREAD);
241 reply("220 Plan 9 FTP server ready");
243 while(cmd = Brdline(&in, '\n')){
247 * strip out trailing cr's & lf and delimit with null
253 while(i > 0 && cmd[i-1] == '\r')
257 * hack for GatorFTP+, look for a 0x10 used as a delimiter
259 p = strchr(cmd, 0x10);
264 * get rid of telnet control sequences (we don't need them)
266 while(*cmd && (uchar)*cmd == Iac){
273 * parse the message (command arg)
275 arg = strchr(cmd, ' ');
283 * ignore blank commands
289 * lookup the command and do it
291 for(p = cmd; *p; p++)
293 for(t = cmdtab; t->name; t++)
294 if(strcmp(cmd, t->name) == 0){
295 if(t->needlogin && !loggedin)
297 else if((*t->f)(arg) < 0)
301 if(t->f != restartcmd){
303 * the file offset is set to zero following
304 * all commands except the restart command
310 * the OOB bytes preceding an abort from UCB machines
311 * comes out as something unrecognizable instead of
312 * IAC's. Certainly a Plan 9 bug but I can't find it.
313 * This is a major hack to avoid the problem. -- presotto
316 if(i > 4 && strcmp(cmd+i-4, "abor") == 0){
319 logit("%s (%s) command not implemented", cmd, arg?arg:"");
320 reply("502 %s command not implemented", cmd);
326 postnote(PNPROC, pid, "kill");
333 reply(char *fmt, ...)
339 s = vseprint(buf, buf+sizeof(buf)-3, fmt, arg);
347 write(1, buf, s - buf);
354 return reply("530 Sod off, service requires login");
358 * run a command in a separate process
361 asproc(void (*f)(char*, int), char *arg, int arg2)
366 /* wait for previous command to finish */
369 if(i == pid || i < 0)
374 switch(pid = rfork(RFFDG|RFPROC|RFNOTEG)){
376 return reply("450 Out of processes: %r");
387 * run a command to filter a tail
390 transfer(char *cmd, char *a1, char *a2, char *a3, int image)
392 int n, dfd, fd, bytes, eofs, pid;
397 reply("150 Opening data connection for %s (%s)", cmd, data);
400 return reply("425 Error opening data connection: %r");
403 return reply("520 Internal Error: %r");
406 switch(pid = rfork(RFFDG|RFPROC|RFNAMEG)){
408 return reply("450 Out of processes: %r");
410 logit("running %s %s %s %s pid %d",
411 cmd, a1?a1:"", a2?a2:"" , a3?a3:"",getpid());
417 fd = open("#s/boot", ORDWR);
419 || bind("#/", "/", MAFTER) < 0
420 || amount(fd, "/bin", MREPL, "") < 0
421 || bind("#c", "/dev", MAFTER) < 0
422 || bind(bindir, "/bin", MREPL) < 0)
423 exits("building name space");
426 execl(cmd, cmd, a1, a2, a3, nil);
431 while((n = read(pfd[1], buf, sizeof buf)) >= 0){
451 n = crlfwrite(dfd, p, n);
453 n = write(dfd, p, n);
455 postnote(PNPROC, pid, "kill");
466 /* wait for this command to finish */
469 if(w == nil || w->pid == pid)
473 if(w != nil && w->msg != nil && w->msg[0] != 0){
476 logit("%s %s %s %s failed %s", cmd, a1?a1:"", a2?a2:"" , a3?a3:"", w->msg);
479 reply("226 Transfer complete");
488 if(arg == 0 || *arg == 0){
489 reply("501 Syntax error in parameters or arguments");
492 if(p = strchr(arg, ' '))
494 if(cistrcmp(arg, "UTF-8") == 0 || cistrcmp(arg, "UTF8") == 0){
495 reply("200 Command okay");
498 reply("502 %s option not implemented", arg);
509 reply("510 Plan 9 FTP daemon still alive");
517 loginuser(char *user, char *nsfile, int gotoslash)
519 logit("login %s %s %s %s", user, mailaddr, nci->rsys, nsfile);
520 if(nsfile != nil && newns(user, nsfile) < 0){
521 logit("namespace file %s does not exist", nsfile);
522 return reply("530 Not logged in: login out of service");
524 getwd(curdir, sizeof(curdir));
529 putenv("service", "ftp");
532 reply("230- If you have problems, send mail to 'postmaster'.");
533 return reply("230 Logged in");
542 sleep(pause); /* deter guessers */
543 if (pause < (1UL << 20))
550 * get a user id, reply with a challenge. The users 'anonymous'
551 * and 'ftp' are equivalent to 'none'. The user 'none' requires
559 logit("user %s %s", name, nci->rsys);
561 return reply("530 Already logged in as %s", user);
562 if(name == 0 || *name == 0)
563 return reply("530 user command needs user name");
569 strncpy(user, name, sizeof(user));
572 user[sizeof(user)-1] = 0;
573 if(strcmp(user, "anonymous") == 0 || strcmp(user, "ftp") == 0)
574 strcpy(user, "none");
575 else if(anon_everybody)
578 if(strcmp(user, "Administrator") == 0 || strcmp(user, "admin") == 0)
579 return reply("530 go away, script kiddie");
580 else if(strcmp(user, "*none") == 0){
582 return reply("530 Not logged in: anonymous disallowed");
583 return loginuser("none", namespace, 1);
585 else if(strcmp(user, "none") == 0){
587 return reply("530 Not logged in: anonymous disallowed");
588 return reply("331 Send email address as password");
591 return reply("530 Not logged in: anonymous access only");
593 isnoworld = noworld(name);
595 return reply("331 OK");
597 /* consult the auth server */
600 if((ch = auth_challenge("proto=p9cr role=server user=%q", user)) == nil)
601 return reply("421 %r");
602 return reply("331 encrypt challenge, %s, as a password", ch->chal);
606 * get a password, set up user if it works.
609 passcmd(char *response)
617 if(strcmp(user, "none") == 0 || strcmp(user, "*none") == 0){
618 /* for none, accept anything as a password */
620 strncpy(mailaddr, response, sizeof(mailaddr)-1);
621 return loginuser("none", namespace, 1);
625 /* noworld gets a password in the clear */
626 if(login(user, response, "/lib/namespace.noworld") < 0)
627 return reply("530 Not logged in");
629 /* login has already setup the namespace */
630 return loginuser(user, nil, 0);
632 /* for everyone else, do challenge response */
634 return reply("531 Send user id before encrypted challenge");
636 ch->nresp = strlen(response);
637 ai = auth_response(ch);
638 if(ai == nil || auth_chuid(ai, nil) < 0) {
640 return reply("530 Not logged in: %r");
645 /* if the user has specified a namespace for ftp, use it */
646 snprint(namefile, sizeof(namefile), "/usr/%s/lib/namespace.ftp", user);
647 strcpy(mailaddr, user);
649 if(access(namefile, 0) == 0)
650 return loginuser(user, namefile, 0);
652 return loginuser(user, "/lib/namespace", 0);
657 * print working directory
663 return reply("550 Pwd takes no argument");
664 return reply("257 \"%s\" is the current directory", curdir);
676 /* shell cd semantics */
677 if(dir == 0 || *dir == 0){
681 snprint(buf, sizeof buf, "/usr/%s", user);
684 if(accessok(rp) == 0)
690 return reply("550 Permission denied");
693 return reply("550 Cwd failed: %r");
695 return reply("250 directory changed to %s", curdir);
714 postnote(PNPROC, pid, "kill");
726 return reply("501 Type command needs arguments");
744 return reply("501 Unimplemented type %s", x);
747 return reply("200 Type %s", type==Tascii ? "Ascii" : "Image");
754 return reply("501 Mode command needs arguments");
756 switch(tolower(*arg)){
761 return reply("501 Unimplemented mode %c", *arg);
765 return reply("200 Stream mode");
772 return reply("501 Struct command needs arguments");
774 switch(tolower(*arg)){
779 return reply("501 Unimplemented structure %c", *arg);
782 return reply("200 File structure");
792 return reply("501 Port command needs arguments");
793 n = getfields(arg, field, 7, 0, ", ");
795 return reply("501 Incorrect port specification");
796 snprint(data, sizeof data, "tcp!%.3s.%.3s.%.3s.%.3s!%d", field[0], field[1], field[2],
797 field[3], atoi(field[4])*256 + atoi(field[5]));
798 return reply("200 Data port is %s", data);
808 if(bind("#/", "/", MAFTER) < 0){
809 logit("can't bind #/ to /: %r");
810 return reply("500 can't bind #/ to /: %r");
813 if(bind(nci->spec, "/net", MBEFORE) < 0){
814 logit("can't bind %s to /net: %r", nci->spec);
815 rv = reply("500 can't bind %s to /net: %r", nci->spec);
846 p->afd = announce("tcp!*!0", passive.adir);
849 return reply("500 No free ports");
851 nnci = getnetconninfo(p->adir, -1);
854 /* parse the local address */
856 logit("local sys is %s", nci->lsys);
857 parseip(p->ipaddr, nci->lsys);
858 if(ipcmp(p->ipaddr, v4prefix) == 0 || ipcmp(p->ipaddr, IPnoaddr) == 0)
859 parseip(p->ipaddr, nci->lsys);
860 p->port = atoi(nnci->lserv);
862 freenetconninfo(nnci);
865 return reply("227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)",
866 p->ipaddr[IPv4off+0], p->ipaddr[IPv4off+1], p->ipaddr[IPv4off+2], p->ipaddr[IPv4off+3],
867 p->port>>8, p->port&0xff);
874 int Cflag, rflag, tflag, Rflag;
884 strcpy(asc, "----------");
892 for(p = asc+1; p < asc + 10; p += 3, m<<=3){
903 listfile(Biobufhdr *b, char *name, int lflag, char *dname)
918 if(strncmp(x, "/incoming/", sizeof("/incoming/")-1) != 0)
924 strcpy(ts, ctime(d->mtime));
927 if(now - d->mtime > 6*30*24*60*60)
928 memmove(ts+11, ts+23, 5);
930 /* Unix style long listing */
937 Bprint(b, "%s %3d %-8s %-8s %7lld %s ",
938 mode2asc(d->mode), links,
939 d->uid, d->gid, d->length, ts+4);
941 if(Cflag && maxnamelen < 40){
943 pad = ((col+maxnamelen)/(maxnamelen+1))*(maxnamelen+1);
944 if(pad+maxnamelen+1 < 60){
945 Bprint(b, "%*s", pad-col+n, name);
949 Bprint(b, "\r\n%s", name);
955 Bprint(b, "%s/", dname);
956 Bprint(b, "%s\r\n", name);
961 dircomp(void *va, void *vb)
970 rv = b->mtime - a->mtime;
972 rv = strcmp(a->name, b->name);
973 return (rflag?-1:1)*rv;
976 listdir(char *name, Biobufhdr *b, int lflag, int *printname, Globlist *gl)
985 fd = open(name, OREAD);
987 Bprint(b, "can't read %s: %r\r\n", name);
993 Bprint(b, "\r\n%s:\r\n", name);
997 n = dirreadall(fd, &p);
1000 for(i = 0; i < n; i++){
1001 l = strlen(p[i].name);
1007 /* Unix style total line */
1010 for(i = 0; i < n; i++){
1011 if(p[i].qid.type & QTDIR)
1014 total += p[i].length;
1016 Bprint(b, "total %ulld\r\n", total/512);
1019 qsort(p, n, sizeof(Dir), dircomp);
1020 for(i = 0; i < n; i++){
1021 if(Rflag && (p[i].qid.type & QTDIR)){
1023 globadd(gl, name, p[i].name);
1025 listfile(b, p[i].name, lflag, dname);
1030 list(char *arg, int lflag)
1047 logit("ls %s (. = %s)", arg, curdir);
1049 /* process arguments, understand /bin/ls -l option */
1051 argv[0] = "/bin/ls";
1052 argc = getfields(arg, argv+1, Narg-2, 1, " \t") + 1;
1081 reply("425 Error opening data connection:%r");
1084 reply("150 Opened data connection (%s)", data);
1086 Binits(&bh, dfd, OWRITE, buf, sizeof(buf));
1093 for(i = 0; i < argc; i++){
1099 printname = gl->first != nil && gl->first->next != nil;
1103 for(g = gl->first; g; g = g->next)
1104 if(g->glob && (n = strlen(s_to_c(g->glob))) > maxnamelen)
1106 while(s = globiter(gl)){
1108 logit("glob %s", s);
1119 if(d->qid.type & QTDIR)
1120 listdir(s, &bh, lflag, &printname, gl);
1122 listfile(&bh, s, lflag, 0);
1129 Bprint(&bh, "\r\n");
1133 reply("226 Transfer complete (list %s)", arg);
1136 namelistcmd(char *arg)
1138 return asproc(list, arg, 0);
1143 return asproc(list, arg, 1);
1147 * fuse compatability
1155 fd = open("#c/user", OREAD);
1158 n = read(fd, buf, sizeof buf - 1);
1161 if(strcmp(buf, "none") == 0)
1176 return reply("501 bad site command");
1177 nf = tokenize(arg, f, nelem(f));
1178 if(nf != 3 || cistrcmp(f[0], "chmod") != 0)
1179 return reply("501 bad site command");
1181 return reply("550 Permission denied");
1184 return reply("501 site chmod: file does not exist");
1186 d->mode |= strtoul(f[1], 0, 8) & 0777;
1187 r = dirwstat(f[2], d);
1190 return reply("550 Permission denied %r");
1191 return reply("200 very well, then");
1195 * return the size of the file
1204 return reply("501 Size command requires pathname");
1208 return reply("501 %r accessing %s", arg);
1209 rv = reply("213 %lld", d->length);
1215 * return the modify time of the file
1225 return reply("501 Mdtm command requires pathname");
1227 return reply("550 Permission denied");
1230 return reply("501 %r accessing %s", arg);
1231 t = gmtime(d->mtime);
1232 rv = reply("213 %4.4d%2.2d%2.2d%2.2d%2.2d%2.2d",
1233 t->year+1900, t->mon+1, t->mday,
1234 t->hour, t->min, t->sec);
1240 * set an offset to start reading a file from
1241 * only lasts for one command
1244 restartcmd(char *arg)
1247 return reply("501 Restart command requires offset");
1248 offset = atoll(arg);
1251 return reply("501 Bad offset");
1254 return reply("350 Restarting at %lld. Send STORE or RETRIEVE", offset);
1258 * send a file to the user
1261 crlfwrite(int fd, char *p, int n)
1266 for(np = buf, ep = p + n; p < ep; p++){
1271 if(write(fd, buf, np - buf) == np - buf)
1277 retrievedir(char *arg)
1284 reply("550 This file requires type binary/image");
1289 p = strrchr(s_to_c(file), '/');
1290 if(p != s_to_c(file)){
1292 chdir(s_to_c(file));
1298 n = transfer("/bin/tar", "c", p, 0, 1);
1300 logit("get %s failed", arg);
1302 logit("get %s OK %d", arg, n);
1306 retrieve(char *arg, int arg2)
1308 int dfd, fd, n, i, bytes;
1315 p = strchr(arg, '\r');
1317 logit("cr in file name", arg);
1321 fd = open(arg, OREAD);
1324 if(n > 4 && strcmp(arg+n-4, ".tar") == 0){
1328 if(d->qid.type & QTDIR){
1336 logit("get %s failed", arg);
1337 reply("550 Error opening %s: %r", arg);
1341 if(seek(fd, offset, 0) < 0){
1342 reply("550 %s: seek to %lld failed", arg, offset);
1348 if(d->qid.type & QTDIR){
1349 reply("550 %s: not a plain file.", arg);
1357 n = read(fd, buf, sizeof(buf));
1359 logit("get %s failed", arg, mailaddr, nci->rsys);
1360 reply("550 Error reading %s: %r", arg);
1366 for(p = buf, ep = &buf[n]; p < ep; p++)
1369 reply("550 This file requires type binary/image");
1373 reply("150 Opening data connection for %s (%s)", arg, data);
1376 reply("425 Error opening data connection:%r");
1385 i = write(dfd, buf, n);
1388 i = crlfwrite(dfd, buf, n);
1394 logit("get %s %r to data connection after %d", arg, bytes);
1395 reply("550 Error writing to data connection: %r");
1399 } while((n = read(fd, buf, sizeof(buf))) > 0);
1402 logit("get %s %r after %d", arg, bytes);
1406 reply("226 Transfer complete");
1407 logit("get %s OK %d", arg, bytes);
1410 retrievecmd(char *arg)
1413 return reply("501 Retrieve command requires an argument");
1416 return reply("550 Permission denied");
1418 return asproc(retrieve, arg, 0);
1422 * get a file from the user
1425 lfwrite(int fd, char *p, int n)
1430 for(np = buf, ep = p + n; p < ep; p++){
1434 if(write(fd, buf, np - buf) == np - buf)
1440 store(char *arg, int fd)
1445 reply("150 Opening data connection for %s (%s)", arg, data);
1448 reply("425 Error opening data connection:%r");
1453 while((n = read(dfd, buf, sizeof(buf))) > 0){
1456 i = write(fd, buf, n);
1459 i = lfwrite(fd, buf, n);
1465 reply("550 Error writing file");
1471 logit("put %s OK", arg);
1472 reply("226 Transfer complete");
1480 return reply("501 Store command requires an argument");
1483 return reply("550 Permission denied");
1484 if(isnone && strncmp(arg, "/incoming/", sizeof("/incoming/")-1))
1485 return reply("550 Permission denied");
1487 fd = open(arg, OWRITE);
1489 return reply("550 Error opening %s: %r", arg);
1490 if(seek(fd, offset, 0) == -1)
1491 return reply("550 Error seeking %s to %d: %r",
1494 fd = create(arg, OWRITE, createperm);
1496 return reply("550 Error creating %s: %r", arg);
1499 rv = asproc(store, arg, fd);
1504 appendcmd(char *arg)
1509 return reply("501 Append command requires an argument");
1511 return reply("550 Permission denied");
1514 return reply("550 Error creating %s: Permission denied", arg);
1515 fd = open(arg, OWRITE);
1517 fd = create(arg, OWRITE, createperm);
1519 return reply("550 Error creating %s: %r", arg);
1523 rv = asproc(store, arg, fd);
1528 storeucmd(char *arg)
1535 return reply("550 Permission denied");
1536 strncpy(name, "ftpXXXXXXXXXXX", sizeof name);
1538 fd = create(name, OWRITE, createperm);
1540 return reply("550 Error creating %s: %r", name);
1542 rv = asproc(store, name, fd);
1548 mkdircmd(char *name)
1553 return reply("501 Mkdir command requires an argument");
1555 return reply("550 Permission denied");
1556 name = abspath(name);
1558 return reply("550 Permission denied");
1559 fd = create(name, OREAD, DMDIR|0775);
1561 return reply("550 Can't create %s: %r", name);
1563 return reply("226 %s created", name);
1570 return reply("501 Rmdir/delete command requires an argument");
1572 return reply("550 Permission denied");
1573 name = abspath(name);
1575 return reply("550 Permission denied");
1576 if(remove(name) < 0)
1577 return reply("550 Can't remove %s: %r", name);
1579 return reply("226 %s removed", name);
1583 * kill off the last transfer (if the process still exists)
1590 logit("abort pid %d", pid);
1592 if(postnote(PNPROC, pid, "kill") == 0)
1593 reply("426 Command aborted");
1595 logit("postnote pid %d %r", pid);
1597 return reply("226 Abort processed");
1601 systemcmd(char *arg)
1604 return reply("215 UNIX Type: L8 Version: Plan 9");
1615 reply("214- the following commands are implemented:");
1619 for(i = 0; cmdtab[i].name; i++){
1621 reply("214-%s", buf);
1624 p = seprint(p, e, " %-5.5s", cmdtab[i].name);
1627 reply("214-%s", buf);
1633 * renaming a file takes two commands
1635 static String *filepath;
1641 return reply("550 Permission denied");
1643 return reply("501 Rename command requires an argument");
1644 from = abspath(from);
1646 return reply("550 Permission denied");
1648 filepath = s_copy(from);
1651 s_append(filepath, from);
1653 return reply("350 Rename %s to ...", s_to_c(filepath));
1663 return reply("550 Permission denied");
1665 return reply("501 Rename command requires an argument");
1668 return reply("550 Permission denied");
1669 if(filepath == nil || *(s_to_c(filepath)) == 0)
1670 return reply("503 Rnto must be preceeded by an rnfr");
1672 tp = strrchr(to, '/');
1673 fp = strrchr(s_to_c(filepath), '/');
1674 if((tp && fp == 0) || (fp && tp == 0)
1675 || (fp && tp && (fp-s_to_c(filepath) != tp-to || memcmp(s_to_c(filepath), to, tp-to))))
1676 return reply("550 Rename can't change directory");
1682 if(dirwstat(s_to_c(filepath), &nd) < 0)
1683 r = reply("550 Can't rename %s to %s: %r\n", s_to_c(filepath), to);
1685 r = reply("250 %s now %s", s_to_c(filepath), to);
1692 * to dial out we need the network file system in our
1706 fd = dial(data, "20", 0, 0);
1707 errstr(err, sizeof err);
1710 cfd = listen(passive.adir, ldir);
1712 errstr(err, sizeof err);
1715 fd = accept(cfd, ldir);
1716 errstr(err, sizeof err);
1720 logit("can't dial %s: %s", data, err);
1723 werrstr(err, sizeof err);
1728 postnote(int group, int pid, char *note)
1734 * Use #p because /proc may not be in the namespace.
1738 sprint(file, "#p/%d/note", pid);
1741 sprint(file, "#p/%d/notepg", pid);
1747 f = open(file, OWRITE);
1752 if(write(f, note, r) != r) {
1761 * to circumscribe the accessible files we have to eliminate ..'s
1762 * and resolve all names from the root. We also remove any /bin/rc
1763 * special characters to avoid later problems with executed commands.
1765 char *special = "`;| ";
1768 abspath(char *origpath)
1770 char *p, *sp, *path;
1771 static String *rpath;
1779 s_append(rpath, curdir);
1781 if(*origpath != '/'){
1782 s_append(rpath, curdir);
1783 s_append(rpath, "/");
1785 s_append(rpath, origpath);
1787 path = s_to_c(rpath);
1789 for(sp = special; *sp; sp++){
1790 p = strchr(path, *sp);
1795 cleanname(s_to_c(rpath));
1796 rpath->ptr = rpath->base+strlen(rpath->base);
1798 if(!accessok(s_to_c(rpath)))
1801 return s_to_c(rpath);
1804 typedef struct Path Path;
1818 Path *pathlevel[Maxlevel];
1821 unlinkpath(char *path, int level)
1828 for(l = &pathlevel[level]; *l; l = &(*l)->next){
1831 if(strcmp(s_to_c(p->path), path) == 0){
1837 if(++n >= Maxperlevel){
1841 memset(p, 0, sizeof *p);
1842 p->path = s_append(s, path);
1848 p = mallocz(sizeof *p, 1);
1849 p->path = s_copy(path);
1854 linkpath(Path *p, int level)
1856 p->next = pathlevel[level];
1857 pathlevel[level] = p;
1862 addpath(Path *p, int level, int ok)
1865 p->next = pathlevel[level];
1866 pathlevel[level] = p;
1870 _accessok(String *s, int level)
1875 static char httplogin[] = "/.httplogin";
1883 p = unlinkpath(s_to_c(s), lvl);
1889 cp = strrchr(s_to_c(s), '/');
1893 offset = cp - s_to_c(s);
1894 s_append(s, httplogin);
1895 if(access(s_to_c(s), AEXIST) == 0){
1901 * There's no way to shorten a String without
1902 * knowing the implementation.
1904 s->ptr = s->base+offset;
1906 addpath(p, lvl, _accessok(s, level-1));
1912 * check for a subdirectory containing .httplogin
1913 * at each level of the path.
1916 accessok(char *path)
1922 npath = s_copy(path);
1923 p = s_to_c(npath)+1;
1924 for(level = 1; level < Maxlevel; level++){
1931 r = _accessok(npath, level-1);