/* file.c Copyright 1992-2000 by Michael Temari All Rights Reserved * * This file is part of ftpd. * * This file handles: * * ALLO APPE CDUP CWD DELE LIST MDTM MODE MKD NLST PWD REST RETR * RMD RNFR RNTO SITE SIZE STAT STOR STOU STRU SYST TYPE * * 01/25/96 Initial Release Michael Temari * 03/09/00 Michael Temari, */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ftpd.h" #include "access.h" #include "file.h" #include "net.h" _PROTOTYPE(static int fdxcmd, (int cmd, char *arg)); _PROTOTYPE(static int endfdxcmd, (int fd)); _PROTOTYPE(static int asciisize, (char *filename, unsigned long *filesize)); _PROTOTYPE(static int cnvtfile, (char *name, char **name2)); _PROTOTYPE(static int procfile, (char *name)); _PROTOTYPE(static unsigned long fsize, (char *fname)); _PROTOTYPE(static int sendfile, (char *name, int xmode)); _PROTOTYPE(static int recvfile, (char *name, int xmode)); _PROTOTYPE(static char *uniqname, (void)); _PROTOTYPE(static int docrc, (char *buff, int xmode)); _PROTOTYPE(static int dofdet, (char *buff)); _PROTOTYPE(static char *path, (char *fname)); #define SEND_FILE 0 #define SEND_NLST 1 #define SEND_LIST 2 #define RECV_FILE 0 #define RECV_APND 1 #define RECV_UNIQ 2 #define CNVT_ERROR 0 #define CNVT_NONE 1 #define CNVT_TAR 2 #define CNVT_TAR_Z 3 #define CNVT_COMP 4 #define CNVT_TAR_GZ 5 #define CNVT_GZIP 6 #define CNVT_UNCOMP 7 #define PROG_FTPDSH "ftpdsh" #define CMD_NLST 1 #define CMD_LIST 2 #define CMD_CRC 3 static char *msg550 = "550 %s %s.\r\n"; static unsigned long file_restart = 0; static char rnfr[256]; static char buffer[8192]; static char bufout[8192]; static cmdpid = -1; /* allocate, we don't need no stink'n allocate */ int doALLO(buff) char *buff; { printf("202 ALLO command not needed at this site.\r\n"); return(GOOD); } /* append to a file if it exists */ int doAPPE(buff) char *buff; { return(recvfile(buff, RECV_APND)); } /* change to parent directory */ int doCDUP(buff) char *buff; { if(ChkLoggedIn()) return(GOOD); return(doCWD("..")); } /* change directory */ int doCWD(buff) char *buff; { if(ChkLoggedIn()) return(GOOD); if(chdir(buff)) printf(msg550, buff, strerror(errno)); else { showmsg("250", ".ftpd_msg"); printf("250 %s command okay.\r\n", line); } return(GOOD); } /* remove a file */ int doDELE(buff) char *buff; { if(ChkLoggedIn()) return(GOOD); if(anonymous) { printf("550 Command not allowed for anonymous user\r\n"); return(GOOD); } if(unlink(buff)) printf(msg550, buff, strerror(errno)); else { printf("250 File \"%s\" deleted.\r\n", buff); logit("DELE", path(buff)); } return(GOOD); } /* directory listing */ int doLIST(buff) char *buff; { file_restart = 0; return(sendfile(buff, SEND_LIST)); } /* file modification time, btw when will this be put into an RFC */ int doMDTM(buff) char *buff; { struct stat st; struct tm *t; if(ChkLoggedIn()) return(GOOD); if(stat(buff, &st)) { printf(msg550, buff, strerror(errno)); return(GOOD); } if((st.st_mode & S_IFMT) != S_IFREG) { printf("550 Not a regular file.\r\n"); return(GOOD); } t = gmtime(&st.st_mtime); printf("215 %04d%02d%02d%02d%02d%02d\r\n", t->tm_year+1900, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); return(GOOD); } /* mode */ int doMODE(buff) char *buff; { switch(*buff) { case 'b': case 'B': printf("200 Mode set to %c.\r\n", *buff); mode = MODE_B; break; case 's': case 'S': printf("200 Mode set to %c.\r\n", *buff); mode = MODE_S; break; default: printf("501 Unknown mode %c.\r\n", *buff); } return(GOOD); } /* make a directory */ int doMKD(buff) char *buff; { if(ChkLoggedIn()) return(GOOD); if(anonymous) { printf("550 Command not allowed for anonymous user\r\n"); return(GOOD); } if(mkdir(buff, 0777)) printf(msg550, buff, strerror(errno)); else { printf("257 \"%s\" directory created.\r\n", buff); logit("MKD ", path(buff)); } return(GOOD); } /* name listing */ int doNLST(buff) char *buff; { file_restart = 0; return(sendfile(buff, SEND_NLST)); } /* where are we */ int doPWD(buff) char *buff; { char dir[128]; if(ChkLoggedIn()) return(GOOD); if(getcwd(dir, sizeof(dir)) == (char *)NULL) printf(msg550, buff, strerror(errno)); else printf("257 \"%s\" is current directory.\r\n", dir); return(GOOD); } /* restart command */ int doREST(buff) char *buff; { if(ChkLoggedIn()) return(GOOD); file_restart = atol(buff); printf("350 Next file transfer will restart at %lu.\r\n", file_restart); return(GOOD); } /* they want a file */ int doRETR(buff) char *buff; { return(sendfile(buff, SEND_FILE)); } /* remove a directory */ int doRMD(buff) char *buff; { if(ChkLoggedIn()) return(GOOD); if(anonymous) { printf("550 Command not allowed for anonymous user\r\n"); return(GOOD); } if(rmdir(buff)) printf(msg550, buff, strerror(errno)); else { printf("250 Directory \"%s\" deleted.\r\n", buff); logit("RMD ", path(buff)); } return(GOOD); } /* rename from */ int doRNFR(buff) char *buff; { if(ChkLoggedIn()) return(GOOD); if(anonymous) { printf("550 Command not allowed for anonymous user\r\n"); return(GOOD); } strncpy(rnfr, buff, sizeof(rnfr)); rnfr[sizeof(rnfr)-1] = '\0'; printf("350 Got RNFR waiting for RNTO.\r\n"); return(GOOD); } /* rename to */ int doRNTO(buff) char *buff; { if(ChkLoggedIn()) return(GOOD); if(anonymous) { printf("550 Command not allowed for anonymous user\r\n"); return(GOOD); } if(rnfr[0] == '\0') { printf("550 Rename failed.\r\n"); return(GOOD); } if(rename(rnfr, buff) < 0) printf("550 Rename failed. Error %s\r\n", strerror(errno)); else { printf("250 Renamed %s to %s.\r\n", rnfr, buff); logit("RNFR", path(rnfr)); logit("RNTO", path(buff)); } rnfr[0] = '\0'; return(GOOD); } /* xmode = 0 for multiline crc, xmode <> 0 for single file single line crc */ static int docrc(buff, xmode) char *buff; int xmode; { unsigned short cs; long fs; int fd; int s; char *p; if((fd = fdxcmd(CMD_CRC, buff)) < 0) { printf("501 Could not obtain CRC.\r\n"); return(GOOD); } if(xmode == 0) printf("202-SITE CRC \"%s\"\r\n", buff); while(1) { p = buffer; while(1) { if((s = read(fd, p, 1)) != 1) { if(xmode == 0) printf("202 SITE CRC DONE.\r\n"); else printf("501 Could not obtain CRC.\r\n"); endfdxcmd(fd); return(GOOD); } if(*p == '\n') { *p++ = '\r'; *p++ = '\n'; *p = '\0'; break; } p++; } if(xmode != 0) break; printf(" %s", buffer); } cs = atoi(buffer); fs = atol(buffer+6); printf("202 CRC %05u %ld.\r\n", cs, fs); endfdxcmd(fd); return(GOOD); } /* site specific */ int doSITE(buff) char *buff; { char *args; if(ChkLoggedIn()) return(GOOD); strncpy(line, buff, sizeof(line)); line[sizeof(line)-1] = '\0'; cvtline(&args); if(!strcmp(line, "CRC") || !strcmp(line, "CCRC")) return(docrc(args, strcmp(line, "CRC"))); if(!strcmp(line, "FDET")) return(dofdet(args)); printf("501 Unknown SITE command %s.\r\n", line); return(GOOD); } static unsigned long fsize(fname) char *fname; { struct stat st; unsigned long fs = 0L; if(stat(fname, &st)) return(fs); if((st.st_mode & S_IFMT) != S_IFREG) return(fs); if(type == TYPE_A) return(fs); fs = st.st_size; return(fs); } /* file size, btw when will this be put into an RFC */ int doSIZE(buff) char *buff; { struct stat st; unsigned long filesize; if(ChkLoggedIn()) return(GOOD); if(stat(buff, &st)) { printf(msg550, buff, strerror(errno)); return(GOOD); } if((st.st_mode & S_IFMT) != S_IFREG) { printf("550 Not a regular file.\r\n"); return(GOOD); } filesize = st.st_size; if(type == TYPE_A) if(asciisize(buff, &filesize)) return(GOOD); printf("215 %lu\r\n", filesize); return(GOOD); } /* server status, or file status */ int doSTAT(buff) char *buff; { time_t now; struct tm *tm; int fd; int s; if(!*buff) { (void) time(&now); tm = localtime(&now); printf("211-%s(%s:%u) FTP server status:\r\n", myhostname, inet_ntoa(myipaddr), ntohs(myport)); printf(" Version %s ", FtpdVersion); printf("%s, %02d %s %d %02d:%02d:%02d %s\r\n", days[tm->tm_wday], tm->tm_mday, months[tm->tm_mon], 1900+tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec, tzname[tm->tm_isdst]); printf(" Connected to %s:%u\r\n", inet_ntoa(rmtipaddr), ntohs(rmtport)); if(!loggedin) printf(" Not logged in\r\n"); else printf(" Logged in %s\r\n", username); printf(" MODE: %s\r\n",(mode == MODE_B) ? "Block" : "Stream"); printf(" TYPE: %s\r\n",(type == TYPE_A) ? "Ascii" : "Binary"); printf("211 End of status\r\n"); return(GOOD); } if(ChkLoggedIn()) return(GOOD); printf("211-Status of %s:\r\n", buff); if((fd = fdxcmd(CMD_LIST, buff)) < 0) printf(" Could not retrieve status"); else { while((s = read(fd, buffer, 1)) == 1) { if(*buffer == '\n') printf("\r\n"); else printf("%c", *buffer); } endfdxcmd(fd); } printf("211 End of status\r\n"); return(GOOD); } /* hey look, we're getting a file */ int doSTOR(buff) char *buff; { return(recvfile(buff, RECV_FILE)); } /* hey, get a file unique */ int doSTOU(buff) char *buff; { return(recvfile(buff, RECV_UNIQ)); } /* structure */ int doSTRU(buff) char *buff; { switch(*buff) { case 'f': case 'F': printf("200 Structure set to %c.\r\n", *buff); break; default: printf("501 Unknown structure %c.\r\n", *buff); } return(GOOD); } /* we're UNIX and proud of it! */ int doSYST(buff) char *buff; { printf("215 UNIX Type: L8\r\n"); return(GOOD); } /* change transfer type */ int doTYPE(buff) char *buff; { if(*(buff+1) != '\0') { printf("501 Syntax error in parameters.\r\n"); return(GOOD); } switch(*buff) { case 'A': case 'a': type = TYPE_A; printf("200 Type set to A.\r\n"); break; case 'I': case 'i': type = TYPE_I; printf("200 Type set to I.\r\n"); break; default: printf("501 Invalid type %c.\r\n", *buff); } return(GOOD); } static int fdxcmd(cmd, arg) int cmd; char *arg; { char xcmd[3]; char *argv[5]; int fds[2]; char *smallenv[] = { "PATH=/bin:/usr/bin:/usr/local/bin", NULL, NULL }; if((smallenv[1] = getenv("TZ")) != NULL) smallenv[1] -= 3; /* ouch... */ sprintf(xcmd, "%d", cmd); argv[0] = PROG_FTPDSH; argv[1] = xcmd; argv[2] = arg; argv[3] = (char *)NULL; if(pipe(fds) < 0) return(-1); if((cmdpid = fork()) < 0) { close(fds[0]); close(fds[1]); return(-1); } if(cmdpid == 0) { /* Child */ close(fds[0]); close(0); open("/dev/null", O_RDONLY); dup2(fds[1], 1); dup2(fds[1], 2); close(fds[1]); sprintf(argv[0], "/bin/%s", PROG_FTPDSH); execve(argv[0], argv, smallenv); sprintf(argv[0], "/usr/bin/%s", PROG_FTPDSH); execve(argv[0], argv, smallenv); sprintf(argv[0], "/usr/local/bin/%s", PROG_FTPDSH); execve(argv[0], argv, smallenv); exit(0); } close(fds[1]); return(fds[0]); } /* Same as close if not cmd child started */ static int endfdxcmd(fd) int fd; { int s; int cs; close(fd); if(cmdpid == -1) return(0); s = waitpid(cmdpid, &cs, 0); cmdpid = -1; return(0); } /* returns -1 = size could not be determined, */ /* 0 = size determined and in filesize */ static int asciisize(filename, filesize) char *filename; unsigned long *filesize; { unsigned long count; int fd; char *p, *pp; int cnt; if((fd = open(filename, O_RDONLY)) < 0) { printf(msg550, filename, strerror(errno)); return(-1); } count = 0; while((cnt = read(fd, buffer, sizeof(buffer))) > 0) { count += cnt; p = buffer; while(cnt > 0) if((pp = memchr(p, '\n', cnt)) != (char *)NULL) { count++; cnt = cnt - 1 - (pp - p); p = pp + 1; } else break; } if(cnt == 0) { *filesize = count; close(fd); return(0); } printf(msg550, filename, strerror(errno)); close(fd); return(-1); } /* see if we need to run a command to convert the file */ static int cnvtfile(name, name2) char *name; char **name2; { struct stat st; static char fname[256]; char *p; int cmode; if(!stat(name, &st)) /* file exists can't be a conversion */ if((st.st_mode & S_IFMT) != S_IFREG) { /* must be regular file */ printf("550 Not a regular file.\r\n"); return(CNVT_ERROR); } else return(CNVT_NONE); if(errno != ENOENT) { /* doesn't exist is okay, others are not */ printf(msg550, name, strerror(errno)); return(CNVT_ERROR); } /* find out what kind of conversion */ strncpy(fname, name, sizeof(fname)); fname[sizeof(fname)-1] = '\0'; p = fname + strlen(fname); cmode = CNVT_ERROR; while(p > fname && cmode == CNVT_ERROR) { if(*p == '.') { if(!strcmp(p, ".tar")) cmode = CNVT_TAR; else if(!strcmp(p, ".tar.Z")) cmode = CNVT_TAR_Z; else if(!strcmp(p, ".Z")) cmode = CNVT_COMP; else if(!strcmp(p, ".tar.gz")) cmode = CNVT_TAR_GZ; else if(!strcmp(p, ".gz")) cmode = CNVT_GZIP; if (cmode != CNVT_ERROR) { /* is there a file to convert? */ *p = '\0'; if (!stat(fname, &st)) break; *p = '.'; cmode = CNVT_ERROR; } } p--; } if(cmode == CNVT_ERROR) { printf(msg550, fname, strerror(errno)); return(CNVT_ERROR); } if(cmode == CNVT_COMP || cmode == CNVT_GZIP || cmode == CNVT_UNCOMP) if((st.st_mode & S_IFMT) != S_IFREG) { printf("550 Not a regular file.\r\n"); return(CNVT_ERROR); } *name2 = fname; return(cmode); } static int procfile(name) char *name; { int cmd; int fd; char *name2; cmd = cnvtfile(name, &name2); switch(cmd) { case CNVT_TAR: fd = fdxcmd(cmd + 10, name2); break; case CNVT_TAR_Z: fd = fdxcmd(cmd + 10, name2); break; case CNVT_COMP: fd = fdxcmd(cmd + 10, name2); break; case CNVT_TAR_GZ: fd = fdxcmd(cmd + 10, name2); break; case CNVT_GZIP: fd = fdxcmd(cmd + 10, name2); break; case CNVT_UNCOMP: fd = fdxcmd(cmd + 10, name2); break; case CNVT_NONE: fd = open(name, O_RDONLY); break; case CNVT_ERROR: default: return(-1); } if(fd < 0) printf(msg550, name, strerror(errno)); return(fd); } /* oh no, they're taking a file */ static int sendfile(name, xmode) char *name; int xmode; { char *fname; int fd, s; time_t datastart, dataend; unsigned long datacount; long kbs; char c; char *p; char *op, *ope; off_t sp; int doascii; unsigned long fs; char block[3]; if(ChkLoggedIn()) return(GOOD); switch(xmode) { case SEND_NLST: fname = "NLST"; fd = fdxcmd(CMD_NLST, name); if(fd < 0) printf(msg550, name, strerror(errno)); break; case SEND_LIST: fname = "LIST"; fd = fdxcmd(CMD_LIST, name); if(fd < 0) printf(msg550, name, strerror(errno)); break; default: fname = name; fd = procfile(name); if(fd < 0) logit("FAIL", path(fname)); else logit("SEND", path(fname)); } if(fd < 0) return(GOOD); /* set file position at approriate spot */ if(file_restart) { if(type == TYPE_A) { sp = 0; while(sp < file_restart) { sp++; s = read(fd, buffer, 1); if(s < 0) { printf(msg550, fname, strerror(errno)); endfdxcmd(fd); file_restart = 0; return(GOOD); } if(s == 0) break; if(*buffer == '\n') sp++; } } else { sp = lseek(fd, file_restart, SEEK_SET); if(sp == -1) { printf(msg550, fname, strerror(errno)); endfdxcmd(fd); file_restart = 0; return(GOOD); } } if(sp != file_restart) { printf("550 File restart point error. %lu not %lu\r\n", sp, file_restart); endfdxcmd(fd); file_restart = 0; return(GOOD); } } file_restart = 0; fs = fsize(fname); if(fs == 0L) printf("%03d File %s okay. Opening data connection.\r\n", ftpdata_fd >= 0 ? 125 : 150, fname); else printf("%03d Opening %s mode data connection for %s (%ld bytes).\r\n", ftpdata_fd >= 0 ? 125 : 150, type == TYPE_A ? "ASCII" : "BINARY", fname, fs); fflush(stdout); #ifdef DEBUG fprintf(logfile, "After 125/150 b4 DataConnect\n"); fflush(logfile); #endif if(DataConnect()) { endfdxcmd(fd); return(GOOD); } #ifdef DEBUG fprintf(logfile, "After DataConnect\n"); fflush(logfile); fprintf(logfile, "ftpd: parent %d start sendfile \n", getpid()); fflush(logfile); #endif /* start transfer */ doascii = (type == TYPE_A) || ((xmode == SEND_LIST) || (xmode == SEND_NLST)); /* per RFC1123 4.1.2.7 */ datacount = 0; time(&datastart); op = bufout; ope = bufout + sizeof(bufout) - 3; while((s = read(fd, buffer, sizeof(buffer))) > 0) { #ifdef DEBUG fprintf(logfile, "sendfile read %d\n", s); fflush(logfile); #endif datacount += s; if(doascii) { p = buffer; while(s-- > 0) { c = *p++; if(c == '\n') { *op++ = '\r'; datacount++; } *op++ = c; if(op >= ope) { if(mode == MODE_B) { block[0] = '\0'; *(u16_t *)&block[1] = htons(op - bufout); write(ftpdata_fd, block, sizeof(block)); } write(ftpdata_fd, bufout, op - bufout); op = bufout; } } } else { if(mode == MODE_B) { block[0] = '\0'; *(u16_t *)&block[1] = htons(s); write(ftpdata_fd, block, sizeof(block)); } s = write(ftpdata_fd, buffer, s); } } if(op > bufout) { if(mode == MODE_B) { block[0] = MODE_B_EOF; *(u16_t *)&block[1] = htons(op - bufout); write(ftpdata_fd, block, sizeof(block)); } write(ftpdata_fd, bufout, op - bufout); } else if(mode == MODE_B) { block[0] = MODE_B_EOF; *(u16_t *)&block[1] = htons(0); write(ftpdata_fd, block, sizeof(block)); } time(&dataend); #ifdef DEBUG fprintf(logfile, "ftpd: parent %d end sendfile \n", getpid()); fflush(logfile); #endif endfdxcmd(fd); if(mode != MODE_B) { close(ftpdata_fd); ftpdata_fd = -1; } if(dataend == datastart) dataend++; kbs = (datacount * 100 / (dataend - datastart)) / 1024; if(s < 0) printf("451 Transfer aborted.\r\n"); else printf("%03d Transfer finished successfully. %ld.%02d KB/s\r\n", mode == MODE_B ? 250 : 226, (long)(kbs / 100), (int)(kbs % 100)); return(GOOD); } static int recvfile(name, xmode) char *name; int xmode; { char *fname; time_t datastart, dataend; unsigned long datacount; long kbs; char c; char *p; char *op, *ope; int fd, oflag; int s; int gotcr; off_t sp; char block[3]; unsigned short cnt; if(ChkLoggedIn()) return(GOOD); fname = name; switch(xmode) { case RECV_APND: oflag = O_WRONLY | O_APPEND; break; case RECV_UNIQ: fname = uniqname(); oflag = O_WRONLY | O_CREAT; break; default: oflag = O_WRONLY | O_CREAT | O_TRUNC; } if(file_restart) oflag = O_RDWR; fd = open(fname, oflag, (anonymous ? 0000:0600)); if(fd < 0) { printf(msg550, fname, strerror(errno)); return(GOOD); } /* log the received file */ logit("RECV", path(fname)); /* set file position at approriate spot */ if(file_restart) { if(type == TYPE_A) { sp = 0; while(sp < file_restart) { sp++; s = read(fd, buffer, 1); if(s < 0) { printf(msg550, fname, strerror(errno)); close(fd); file_restart = 0; return(GOOD); } if(s == 0) break; if(*buffer == '\n') sp++; } } else { sp = lseek(fd, file_restart, SEEK_SET); if(sp == -1) { printf(msg550, fname, strerror(errno)); close(fd); file_restart = 0; return(GOOD); } } if(sp != file_restart) { printf("550 File restart point error. %lu not %lu\r\n", sp, file_restart); close(fd); file_restart = 0; return(GOOD); } } file_restart = 0; if(xmode == RECV_UNIQ) printf("%03d FILE: %s\r\n", ftpdata_fd >= 0 ? 125 : 150, fname); /* per RFC1123 4.1.2.9 */ else printf("%03d File %s okay. Opening data connection.\r\n", ftpdata_fd >= 0 ? 125 : 150, fname); fflush(stdout); if(DataConnect()) { close(fd); return(GOOD); } #ifdef DEBUG fprintf(logfile, "ftpd: parent %d start recvfile \n", getpid()); fflush(logfile); #endif /* start receiving file */ datacount = 0; gotcr = 0; op = bufout; ope = bufout + sizeof(bufout) - 3; cnt = 0; time(&datastart); while(1) { if(mode != MODE_B) cnt = sizeof(buffer); else if(cnt == 0) { s = read(ftpdata_fd, block, sizeof(block)); cnt = ntohs(*(u16_t *)&block[1]); s = 0; if(cnt == 0 && block[0] & MODE_B_EOF) break; } s = read(ftpdata_fd, buffer, cnt > sizeof(buffer) ? sizeof(buffer) : cnt); if(s <= 0) break; cnt -= s; datacount += (long)s; if(type == TYPE_A) { p = buffer; while(s-- > 0) { c = *p++; if(gotcr) { gotcr = 0; if(c != '\n') *op++ = '\r'; } if(c == '\r') gotcr = 1; else *op++ = c; if(op >= ope) { write(fd, bufout, op - bufout); op = bufout; } } } else write(fd, buffer, s); if(cnt == 0 && mode == MODE_B && block[0] & MODE_B_EOF) { s = 0; break; } } if(gotcr) *op++ = '\r'; if(op > bufout) write(fd, bufout, op - bufout); time(&dataend); #ifdef DEBUG fprintf(logfile, "ftpd: parent %d end recvfile \n", getpid()); fflush(logfile); #endif close(fd); if(mode != MODE_B) { close(ftpdata_fd); ftpdata_fd = -1; } if(dataend == datastart) dataend++; kbs = (datacount * 100 / (dataend - datastart)) / 1024; if((mode == MODE_B && cnt != 0) || s != 0) printf("451 Transfer aborted.\r\n"); else { printf("%03d Transfer finished successfully. ", mode == MODE_B ? 250 : 226); if(xmode == RECV_UNIQ) printf("Unique file %s. ", fname); printf("%ld.%02d KB/s\r\n", (long)(kbs / 100), (int)(kbs % 100)); } return(GOOD); } static char *uniqname() { static char uniq[32]; int i; struct stat st; for(i = 0; i < 1000; i++) { sprintf(uniq, "ftpd%d%d", getpid(), i); if(stat(uniq, &st) == -1) return(uniq); } return(uniq); } static char *spath[256]; static char *path(fname) char *fname; { char dir[128]; if(getcwd(dir, sizeof(dir)) == (char *)NULL) sprintf(dir, "???"); if(fname[0] == '/') sprintf((char *)spath, "%s%s", newroot, fname); else if(dir[1] == '\0') sprintf((char *)spath, "%s%s%s", newroot, dir, fname); else sprintf((char *)spath, "%s%s/%s", newroot, dir, fname); return((char *)spath); } /* do file detail */ static int dofdet(buff) char *buff; { struct stat st; char ft; if(ChkLoggedIn()) return(GOOD); if(stat(buff, &st)) { printf("501 Could not obtain file detail.\r\n"); return(GOOD); } switch(st.st_mode & S_IFMT) { case S_IFIFO: ft = 'p'; break; case S_IFCHR: ft = 'c'; break; case S_IFDIR: ft = 'd'; break; case S_IFBLK: ft = 'b'; break; case S_IFREG: ft = 'f'; break; default: ft = '?'; break; } printf("202 %c %u %u %u %u %u %lu %lu\r\n", ft, /* file type */ st.st_rdev >> 8, /* Major */ st.st_rdev & 0xff, /* Minor */ st.st_uid, /* UID */ st.st_gid, /* GID */ st.st_mode, /* File Modes */ st.st_size, /* SIZE */ st.st_mtime); /* Mod Time */ return(GOOD); }