static void do_nlst(session_t *sess) { if(get_transfer_fd(sess) == 0) return; ftp_reply(sess, FTP_DATACONN, "Here comes the directory listing."); list_common(sess, 0); close(sess->data_fd); sess->data_fd = -1; ftp_reply(sess, FTP_TRANSFEROK, "Directory send OK."); }
//获取目录详细清单 void do_cmd_list(session_t *pses) { //创建数据连接 if(get_transfer_fd(pses) < 0) return; //150 ftp_reply(pses->ctrl_fd, FTP_DATACONN, "Here comes the directory listing."); //传输列表 getdirlist(pses,1); //关闭数据套接字 close(pses->data_fd); pses->data_fd = -1; //226 ftp_reply(pses->ctrl_fd, FTP_TRANSFEROK, "Directory send OK."); }
static void do_list(session_t *sess) { if(get_transfer_fd(sess) == 0) return; ftp_reply(sess, FTP_DATACONN, "Here comes the directory listing."); //transfer list list_common(sess, 1); //client will still wait the data from server if the fd do not closed close(sess->data_fd); sess->data_fd = -1; ftp_reply(sess, FTP_TRANSFEROK, "Directory send OK."); }
/** *do_nlst - 处理列出目录列表命令,可用windows登录,命令短清单 *@sess:会话结构体 */ void ftpproto::do_nlst(session_t* sess) { //创建数据连接,主动用connect,被动用accept if (get_transfer_fd(sess))//连接失败 { LCWFTPD_LOG(DEBUG,"get_transfer_fd(sess)"); return; } //150响应 ftp_reply(sess,FTP_DATACONN,"Here comes the directory listing."); //传输列表 list_common(sess,0);//0表示短清单 //关闭数据链接套接字,客户端貌似是通过判断套接字关闭从而判断数据是否接收完毕 close(sess->data_fd); sess->data_fd = -1; //226响应 ftp_reply(sess,FTP_TRANSFEROK,"Directory send OK."); }
static void do_retr(session_t *sess) { if(get_transfer_fd(sess) == 0) return; int fd = open(sess->arg, O_RDONLY); if(fd == -1) { ftp_reply(sess, FTP_FILEFAIL, "1Failed to open file."); return; } int ret; ret = lock_file_read(fd); if(ret == -1) { ftp_reply(sess, FTP_FILEFAIL, "Failed to open file."); return; } //device file can not be downloaded struct stat sbuf; ret = fstat(fd, &sbuf); if(!S_ISREG(sbuf.st_mode)) { ftp_reply(sess, FTP_FILEFAIL, "Failed to open file."); return; } long long offset = sess->restart_pos; sess->restart_pos = 0; if(offset != 0) { ret = lseek(fd, offset, SEEK_SET); if(ret == -1) { ftp_reply(sess, FTP_FILEFAIL, "Failed to open file."); return; } } char text[1024] = {0}; if(sess->is_ascii) { sprintf(text, "Opening ASCII mode data conection for %s (%lld bytes).", sess->arg, (long long)sbuf.st_size); } else { sprintf(text, "Opening BINARY mode data conection for %s (%lld bytes).", sess->arg, (long long)sbuf.st_size); } ftp_reply(sess, FTP_DATACONN, text); int flag = 0; long long bytes_to_send = sbuf.st_size; if(offset > bytes_to_send) bytes_to_send = 0; else bytes_to_send -= offset; sess->bw_transfer_start_sec = get_time_sec(); sess->bw_transfer_start_usec = get_time_usec(); while(bytes_to_send) { int num_this_time = bytes_to_send > 65536 ? 65536 : bytes_to_send; ret = sendfile(sess->data_fd, fd, NULL, num_this_time); if(ret == -1) { flag = 2; break; } limit_rate(sess, ret, 0); if(sess->abor_received) { flag = 2; break; } bytes_to_send -= ret; } close(sess->data_fd); sess->data_fd = -1; close(fd); if(flag == 0) ftp_reply(sess, FTP_TRANSFEROK, "Transfer complete."); else if(flag == 1) ftp_reply(sess, FTP_BADSENDFILE, "Failure reading from local file."); else if(flag == 2) ftp_reply(sess, FTP_BADSENDNET, "Failure writing to network stream."); check_abor(sess); start_cmdio_alarm(); }
void upload_common(session_t* sess, int is_append) { if(get_transfer_fd(sess) == 0) return; int fd = open(sess->arg, O_CREAT | O_WRONLY, 0666); if(fd == -1) { ftp_reply(sess, FTP_UPLOADFAIL, "Could not creat file."); return; } int ret = 0; ret = lock_file_write(fd); if(ret == -1) { ftp_reply(sess, FTP_UPLOADFAIL, "Could not creat file."); return; } long long offset = sess->restart_pos; sess->restart_pos = 0; if(!is_append && offset == 0) //STOR { ftruncate(fd, 0); if(lseek(fd, 0, SEEK_SET) < 0) { ftp_reply(sess, FTP_UPLOADFAIL, "Could not creat file."); return; } } else if(!is_append && offset != 0) //REST + STOR { if(lseek(fd, offset, SEEK_SET) < 0) { ftp_reply(sess, FTP_UPLOADFAIL, "Could not creat file."); return; } } else if(is_append) { if(lseek(fd, 0, SEEK_END) < 0) { ftp_reply(sess, FTP_UPLOADFAIL, "Could not creat file."); return; } } struct stat sbuf; fstat(fd, &sbuf); char text[1024] = {0}; if(sess->is_ascii) { sprintf(text, "Opening ASCII mode data conection for %s (%lld bytes).", sess->arg, (long long)sbuf.st_size); } else { sprintf(text, "Opening BINARY mode data conection for %s (%lld bytes).", sess->arg, (long long)sbuf.st_size); } ftp_reply(sess, FTP_DATACONN, text); int flag = 0; char buf[65536] = {0}; sess->bw_transfer_start_sec = get_time_sec(); sess->bw_transfer_start_usec = get_time_usec(); while(1) { ret = read(sess->data_fd, buf, sizeof(buf)); if(ret == -1) { if(errno == EINTR) continue; flag = 2; break; } else if(ret == 0) { flag = 0; break; } limit_rate(sess, ret, 1); if(sess->abor_received) { flag = 2; break; } if(writen(fd, buf, ret) != ret) { flag = 1; break; } } close(sess->data_fd); sess->data_fd = -1; close(fd); if(flag == 0) ftp_reply(sess, FTP_TRANSFEROK, "Transfer complete."); else if(flag == 1) ftp_reply(sess, FTP_BADSENDFILE, "Failure to write file."); else if(flag == 2) ftp_reply(sess, FTP_BADSENDNET, "Failure read from network stream."); check_abor(sess); start_cmdio_alarm(); }
/** *upload_common - 上传文件 *@sess:会话结构体 *@is_append:是否以appe方式上传,0表示STOR方式,1表示APPE方式 */ void ftpproto::upload_common(session_t* sess,int is_append) { //创建数据连接,主动用connect,被动用accept if (get_transfer_fd(sess))//连接失败 { LCWFTPD_LOG(DEBUG,"get_transfer_fd(sess)"); return; } //保存断点,REST命令保存断点 long long offset = sess->restart_pos; sess->restart_pos = 0;//断点置为0 //以创建,只写的方式打开文件,权限默认0666,实际的权限时0666 & umask int fd = open(sess->arg,O_CREAT | O_WRONLY ,0666);//以只读方式打开文件 LCWFTPD_LOG(DEBUG,"file name:%s",sess->arg);//文件名是有带路径的 if (-1 == fd) { //创建失败 ftp_reply(sess,FTP_UPLOADFAIL,"Could not cteate file."); return; } int ret; //加写锁 ret = lcw_systools.lock_file_write(fd); if (-1 ==ret) { ftp_reply(sess,FTP_UPLOADFAIL,"Could not cteate file."); return; } //查看下是什么方式上传 if (!is_append && offset == 0)//STOR上传 { //把原来文件的长度清零 ftruncate(fd,0); if (lseek(fd,0,SEEK_SET) < 0)//文件定位到文件头的位置 { ftp_reply(sess,FTP_UPLOADFAIL,"Could not cteate file."); return; } } else if (!is_append && offset != 0)//REST+STOR 断点续传 { if (lseek(fd,offset,SEEK_SET) < 0)//将读写位置指向文件头后再增加offset个位移量 { ftp_reply(sess,FTP_UPLOADFAIL,"Could not cteate file."); return; } } else if (is_append)//APPE 断点续传 {//将读写位置指向文件尾后再增加offset个位移量 if (lseek(fd,0,SEEK_END) < 0)//偏移到文件末尾 { ftp_reply(sess,FTP_UPLOADFAIL,"Could not cteate file."); return; } } //获取文件状态 struct stat sbuf; ret = fstat(fd,&sbuf);//将文件状态保存在sbuf中 if (!S_ISREG(sbuf.st_mode)) {//不是一个普通文件 ftp_reply(sess,FTP_UPLOADFAIL,"Could not create file."); return; } char text[1024] = {0}; if (sess->is_ascii)//TYPE A { //ASCII码模式(实际上我们这里都是以二进制方式进行传输) sprintf(text,"Opening ASCII mode data connection for %s (%lld bytes).", sess->arg,(long long)sbuf.st_size); } else//TYPE I { //二进制模式 sprintf(text,"Opening BINARY mode data connection for %s (%lld bytes).", sess->arg,(long long)sbuf.st_size); } //150响应 ftp_reply(sess,FTP_DATACONN,text); //上传文件 //这个buf变大的时候传输速度可相应有所提高 //可提供一个配置项来配置这个65536//64K char buf[1024]; int flag = 0;//标志变量 while(1) { ret = read(sess->data_fd,buf,sizeof(buf));//从数据套接字中读取数据到buf if (-1 == ret) { if (errno == EINTR)//被信号中断 { continue; } else { flag = 2;//失败 break; } } else if (0 == ret) {//读取完毕 flag = 0;//成功 break; } if (lcw_systools.writen(fd,buf,ret) != ret)//写入文件 { flag = 1;//失败 break; } } //关闭数据链接套接字,客户端貌似是通过判断套接字关闭从而判断数据是否接收完毕 close(sess->data_fd); sess->data_fd = -1; //关闭文件 close(fd); if (0 == flag)//成功 { //226响应 ftp_reply(sess,FTP_TRANSFEROK,"Transfer complete."); } else if (1 == flag) {//写入本地失败 451 ftp_reply(sess,FTP_BADSENDFILE,"Failure writting to local file."); } else if (2 == flag) {//读取网络失败 426 ftp_reply(sess,FTP_BADSENDNET,"Failure reading from network stream."); } }
/** *do_retr - 下载文件,断点续载 *@sess:会话结构体 */ void ftpproto::do_retr(session_t* sess) { //创建数据连接,主动用connect,被动用accept if (get_transfer_fd(sess))//连接失败 {//get_transfer_fd里面会开启数据通道的传输闹钟,等下传输结束记得关掉 LCWFTPD_LOG(DEBUG,"get_transfer_fd(sess)"); return; } LCWFTPD_LOG(DEBUG,"data_fd int do_retr:%d",sess->data_fd); //保存断点 long long offset = sess->restart_pos; sess->restart_pos = 0;//断点位置为0 int fd = open(sess->arg,O_RDONLY);//以只读方式打开文件 if (-1 == fd) { //打开失败 ftp_reply(sess,FTP_FILEFAIL,"Failed to open file."); return; } int ret; //加读锁 ret = lcw_systools.lock_file_read(fd); if (-1 ==ret) {//加锁失败 ftp_reply(sess,FTP_FILEFAIL,"Failed to open file."); } //判断是否是普通文件,设备文件是不能下载的 struct stat sbuf; ret = fstat(fd,&sbuf);//将文件状态保存在sbuf中 if (!S_ISREG(sbuf.st_mode)) {//不是一个普通文件 ftp_reply(sess,FTP_FILEFAIL,"Failed to open file."); return; } if (offset != 0)//如果有断点 { ret = lseek(fd,offset,SEEK_SET);//定位断点 if (-1 == ret) { //定位失败 ftp_reply(sess,FTP_FILEFAIL,"Failed to lseek"); return; } } char text[1024] = {0}; if (sess->is_ascii)//TYPE A { //ASCII码模式(实际上我们这里都是以二进制方式进行传输) sprintf(text,"Opening ASCII mode data connection for %s (%lld bytes).", sess->arg,(long long)sbuf.st_size); } else//TYPE I { //二进制模式 sprintf(text,"Opening BINARY mode data connection for %s (%lld bytes).", sess->arg,(long long)sbuf.st_size); } //150响应 ftp_reply(sess,FTP_DATACONN,text); //下载文件 int flag = 0;//标志变量 //sendfile直接在内核空间操作,不涉及拷贝,效率较高 long long bytes_to_send = sbuf.st_size;//文件大小 if (offset > bytes_to_send)//断点位置不对 { bytes_to_send = 0; } else { bytes_to_send -= offset;//只传输断点到文件结束的大小 } //开始传输 while(bytes_to_send) { int num_this_time = bytes_to_send > 4096 ? 4096:bytes_to_send; ret = sendfile(sess->data_fd,fd,NULL,num_this_time);//不会返回EINTR,ret是发送成功的字节数 if (-1 == ret) { //发送失败 flag = 2; break; } bytes_to_send -= ret;//更新剩下的字节数 } if (bytes_to_send == 0) { //发送成功 flag = 0; } //关闭数据链接套接字,客户端貌似是通过判断套接字关闭从而判断数据是否接收完毕 close(sess->data_fd); sess->data_fd = -1; //关闭文件 close(fd); if (0 == flag)//成功并且没有收到abor { //226响应 ftp_reply(sess,FTP_TRANSFEROK,"Transfer complete."); } else if (1 == flag) {//文件读取失败 451 ftp_reply(sess,FTP_BADSENDFILE,"Failure reading from local file."); } else if (2 == flag) {//文件发送失败 426 ftp_reply(sess,FTP_BADSENDNET,"Failure writting to network stream."); } }
//上传文件 int upload_common(session_t *pses, int fd, int is_append) { int iret; //创建数据连接 if(get_transfer_fd(pses) < 0) return -3; long long offset = 0; if(pses->restart_pos > 0) //是否需要断点续传 { offset = pses->restart_pos; pses->restart_pos = 0; } //判定文件上传模式 if(!is_append && (offset == 0)) { //STOR 模式 ftruncate(fd, 0); iret = lseek(fd, 0, SEEK_SET); if(iret < 0) { ftp_reply(pses->ctrl_fd, FTP_UPLOADFAIL, "Could not create file."); return -3; } } else if(!is_append && (offset > 0)) { //REST + STOR iret = lseek(fd, offset, SEEK_SET); if(iret < 0) { ftp_reply(pses->ctrl_fd, FTP_UPLOADFAIL, "Could not create file."); return -3; } } else if(is_append) { //APPE iret = lseek(fd, 0, SEEK_END); if(iret < 0) { ftp_reply(pses->ctrl_fd, FTP_UPLOADFAIL, "Could not create file."); return -3; } } //获取文件信息 struct stat sbuf; iret = fstat(fd, &sbuf); if(iret < 0) { printf("fstat error\n"); return -3; } //150 应答 char sendbuf[1024] = {0}; if(pses->is_ascii) { sprintf(sendbuf, "Opening ASCII mode data connection for %s (%lld bytes).", pses->arg, (long long)sbuf.st_size); } else { sprintf(sendbuf, "Opening BINARY mode data connection for %s (%lld bytes).", pses->arg, (long long)sbuf.st_size); } ftp_reply(pses->ctrl_fd, FTP_DATACONN, sendbuf); //上传文件 int flag = 0; char recvbuf[1024]; while(true) { memset(recvbuf, 0, sizeof(recvbuf)); iret = readn(pses->data_fd, recvbuf, sizeof(recvbuf)); if(iret < 0) { if(errno == EINTR) { continue; } else { flag = -2; break; } } else if(iret == 0) { flag = 0; break; } if(writen(fd, recvbuf, sizeof(recvbuf)) != iret) { flag = -1; break; } } return flag; }
void do_cmd_retr(session_t *pses) { //创建数据连接 if(get_transfer_fd(pses) < 0) return; long long offset = 0; if(pses->restart_pos > 0) //是否需要断点续传 { offset = pses->restart_pos; pses->restart_pos = 0; } //打开文件 int fd = open(pses->arg, O_RDONLY); if(fd < 0) { ftp_reply(pses->ctrl_fd, FTP_FILEFAIL, "Failed to open file."); return; } //给文件加读锁 int iret = lock_file_read(fd); if(iret == -1) { ftp_reply(pses->ctrl_fd, FTP_FILEFAIL, "Failed to open file."); return; } //判定文件属性是否是普通文件 struct stat sbuf; iret = fstat(fd, &sbuf); if(!S_ISREG(sbuf.st_mode)) { ftp_reply(pses->ctrl_fd, FTP_FILEFAIL, "Failed to open file."); return; } //定位到断点 if(offset > 0) { iret = lseek(fd, offset, SEEK_SET); if(iret < 0) { ftp_reply(pses->ctrl_fd, FTP_FILEFAIL, "Failed to open file."); return; } } //150 应答 char sendbuf[1024] = {0}; if(pses->is_ascii) { sprintf(sendbuf, "Opening ASCII mode data connection for %s (%lld bytes).", pses->arg, (long long)sbuf.st_size); } else { sprintf(sendbuf, "Opening BINARY mode data connection for %s (%lld bytes).", pses->arg, (long long)sbuf.st_size); } ftp_reply(pses->ctrl_fd, FTP_DATACONN, sendbuf); //传输文件 int flag = 0; long long bytes_to_send = sbuf.st_size; if(offset > bytes_to_send) //客户端发来的断点位置超过了文件长度 { bytes_to_send = 0; } else { bytes_to_send -= offset; } //发送数据 while(bytes_to_send > 0) { int num_this_time = bytes_to_send > 4096 ? 4096 : bytes_to_send; iret = sendfile64(pses->data_fd, fd, NULL, num_this_time); if(iret < 0) { flag = -2; break; } bytes_to_send -= iret; } //发送成功 if(bytes_to_send == 0) { flag = 0; } //关闭数据套接字 close(pses->data_fd); pses->data_fd = -1; //关闭文件 unlock_file(fd); close(fd); if(flag == 0) { //226 ftp_reply(pses->ctrl_fd, FTP_TRANSFEROK, "Transfer complete."); } else if(flag == -2) { //451 ftp_reply(pses->ctrl_fd, FTP_BADSENDNET, "Failure writting to network stream."); } }