tuyau

Client/server for transfering files (like cp)
git clone https://noulin.net/git/tuyau.git
Log | Files | Refs

commit 7b369dafb749ba6bf6442e7440146b834a8b9600
parent 3f5d85a450d7748e9ad5d3b165db9c10e8f8750c
Author: Remy Noulin <loader2x@gmail.com>
Date:   Sat, 13 Aug 2022 11:38:06 -0400

add receive single file from server

Move makeHeader to own file because this function is shared between
client and server

NOTES.md     |  15 ++-
makeHeader.c |  81 ++++++++++++
makeHeader.h |   3 +
sclient.c    | 424 +++++++++++++++++++++++++++++++++++------------------------
sserver.c    |  68 +++++++++-
5 files changed, 416 insertions(+), 175 deletions(-)

Diffstat:
MNOTES.md | 15+++++++++++----
AmakeHeader.c | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AmakeHeader.h | 3+++
Msclient.c | 424+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Msserver.c | 68+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
5 files changed, 416 insertions(+), 175 deletions(-)

diff --git a/NOTES.md b/NOTES.md @@ -7,12 +7,13 @@ x./sclient.c NOTES.md dev:tuyauTest/ x./sclient.c NOTES.md dev:tuyauTest/ger.md ## receive -./sclient.c dev:NOTES.md -./sclient.c dev:NOTES.md ../ +x./sclient.c dev:NOTES.md +x./sclient.c dev:NOTES.md ../ +x./sclient.c dev:NOTES.md test.txt cp file tuyau://ip/path -frame send file: +0 frame send file: 1 byte command 8 bytes token 2 bytes file name length @@ -24,13 +25,19 @@ frame send file: 8 bytes data size <data> -frame mkdir +1 frame mkdir 1 byte command 8 bytes token 2 bytes file mode/permissions 2 bytes path length <path> +2 frame receive file: +1 byte command +8 bytes token +2 bytes path in server length +<path in server> + > client/server requests client > command diff --git a/makeHeader.c b/makeHeader.c @@ -0,0 +1,81 @@ +#include "libsheepyObject.h" +#include "shpPackages/short/short.h" + +void makeHeader(u8 *buf, u32 *bufip, u8 command, smallDictt *svrInfo, char *sendFile, char *destPath, u64 **filesize) { + + // command + // token + // file name max 8k + // file mode + // dest path + + lv(sendFile); + lv(destPath); + + // command + buf[0] = command; + // token + if (svrInfo) + memcpy(buf + 1, $(svrInfo, "token"), 8); + else + // used for downloading files from server to client + pError0(zeroBuf(buf + 1, 8)); + + u32 bufi = 0; + if (command == 0) { + // send file + // file name max 8k + u16 *filenameLength = (u16*)(buf + 9); + char *filename = basename(sendFile); + *filenameLength = lenG(filename); + memcpy(buf + 11, filename, lenG(filename)); + bufi = 11 + lenG(filename); + // file mode + u16 *filemode = (u16*)(buf + bufi); + struct stat st; + if(stat(sendFile, &st) != 0){ + logE("Can't access %s", sendFile); + XFailure; + } + *filemode = st.st_mode & 0xFFF; + bufi += 2; + // dest path + u16 *pathLength = (u16*)(buf + bufi); + *pathLength = lenG(destPath); + bufi += 2; + if (destPath) { + memcpy(buf + bufi, destPath, *pathLength); + bufi += *pathLength; + } + // file size + *filesize = (u64*)(buf + bufi); + **filesize = fileSizeG(sendFile); + bufi += 8; + } + elif (command == 1) { + // mkdir + u16 *filemode = (u16*)(buf + 9); + struct stat st; + if(stat(sendFile, &st) != 0){ + logE("Can't access %s", sendFile); + XFailure; + } + *filemode = st.st_mode & 0xFFF; + // dest path + u16 *pathLength = (u16*)(buf + 11); + *pathLength = lenG(destPath); + memcpy(buf + 13, destPath, *pathLength); + bufi = 13 + lenG(destPath); + } + elif (command == 2) { + // download/receive files + // path in server + u16 *pathLength = (u16*)(buf + 9); + *pathLength = lenG(destPath); + memcpy(buf + 11, destPath, *pathLength); + bufi = 11 + lenG(destPath); + } + + *bufip = bufi; +} +// vim: set expandtab ts=2 sw=2: diff --git a/makeHeader.h b/makeHeader.h @@ -0,0 +1,3 @@ +#include "libsheepyObject.h" +void makeHeader(u8 *buf, u32 *bufip, u8 command, smallDictt *svrInfo, char *sendFile, char *destPath, u64 **filesize); +// vim: set expandtab ts=2 sw=2: diff --git a/sclient.c b/sclient.c @@ -16,74 +16,11 @@ #include "netFrame.h" #include "shpPackages/short/short.h" +#include "makeHeader.h" #define CONFIG "~/.tuyau/config.yml" -void makeHeader(u8 *buf, u32 *bufip, u8 command, smallDictt *svrInfo, char *sendFile, char *destPath, u64 **filesize) { - - // command - // token - // file name max 8k - // file mode - // dest path - - lv(sendFile); - lv(destPath); - - // command - buf[0] = command; - // token - memcpy(buf + 1, $(svrInfo, "token"), 8); - - u32 bufi = 0; - if (command == 0) { - // send file - // file name max 8k - u16 *filenameLength = (u16*)(buf + 9); - char *filename = basename(sendFile); - *filenameLength = lenG(filename); - memcpy(buf + 11, filename, lenG(filename)); - bufi = 11 + lenG(filename); - // file mode - u16 *filemode = (u16*)(buf + bufi); - struct stat st; - if(stat(sendFile, &st) != 0){ - logE("Can't access %s", sendFile); - XFailure; - } - *filemode = st.st_mode & 0xFFF; - bufi += 2; - // dest path - u16 *pathLength = (u16*)(buf + bufi); - *pathLength = lenG(destPath); - bufi += 2; - if (destPath) { - memcpy(buf + bufi, destPath, *pathLength); - bufi += *pathLength; - } - // file size - *filesize = (u64*)(buf + bufi); - **filesize = fileSizeG(sendFile); - bufi += 8; - } - elif (command == 1) { - // mkdir - u16 *filemode = (u16*)(buf + 9); - struct stat st; - if(stat(sendFile, &st) != 0){ - logE("Can't access %s", sendFile); - XFailure; - } - *filemode = st.st_mode & 0xFFF; - // dest path - u16 *pathLength = (u16*)(buf + 11); - *pathLength = lenG(destPath); - memcpy(buf + 13, destPath, *pathLength); - bufi = 13 + lenG(destPath); - } - - *bufip = bufi; -} +bool saveReceivedData(void *receiveB, size_t sz, void *context); int main(int ARGC, char** ARGV) { @@ -99,30 +36,26 @@ int main(int ARGC, char** ARGV) { ret 1; } - // check args - if (ARGC == 2) { - TODO("check that a server is selected and store files in current path"); - } - - if (ARGC < 3) { - logE("Missing arguments"); - ret 1; - } - bool isReceive = no; + const char *serverName = null; + char *destPath = null; + char *receivePath = null; iter(cfg, D) { cast(smallDictt*,d,D); //lv(d); if (startsWithG(ARGV[1], iK(cfg)) and ARGV[1][lenG(iK(cfg))] == ':' and ARGV[1][lenG(iK(cfg))+1] != 0) { - TODO("arg 2 must be a path in client"); - isReceive = yes; + serverName = iK(cfg); + destPath = ARGV[1] + lenG(iK(cfg)) + 1; + isReceive = yes; break; } } - const char *serverName = null; - char *destPath = null; if (not isReceive) { + if (ARGC == 2) { + logE("Missing arguments"); + ret 1; + } logD("path in client"); // check last arg bool foundDest = no; @@ -146,6 +79,26 @@ int main(int ARGC, char** ARGV) { } } } // not isReceive + elif (ARGC > 2) { + // receive + // arg 2 must be a path in client + receivePath = ARGV[2]; + cleanCharP(localPath) = normalizePathG(ARGV[2]); + if (not isEmptyG(localPath)/*ignore "." -> ""*/) { + logD("check if receivePath is dir or dir with filename"); + if (not isPath(localPath)) { + if (endsWithG(receivePath,'/')) { + logE("Directory doesn't exist %s", localPath); + ret 1; + } + cleanCharP(dir) = shDirname(localPath); + if (not isPath(dir)) { + logE("Directory doesn't exist %s", dir); + ret 1; + } + } + } + } if (not serverName) { logE("server name not found"); @@ -159,7 +112,6 @@ int main(int ARGC, char** ARGV) { int sock; struct sockaddr_in server; struct hostent *hp; - int mysock; int rval; cleanAllocateNetFrame(netframe); @@ -190,122 +142,68 @@ int main(int ARGC, char** ARGV) { u8 buf[1024*1024] = init0Var; - cleanAllocateSmallArray(pathToSend); + if (isReceive) { + // receive files in receivePath - rangeFrom(i, 1, ARGC-1) { - if (not isPath(ARGV[i])) { - logE("Incorrect path: "BLD YLW"%s"RST,ARGV[i]); - continue; - } - pushG(pathToSend, ARGV[i]); - } - - cleanAllocateSmallDict(recusive); + u32 bufi = 0; + makeHeader(buf, &bufi, 2 /* command */, svrInfo, null /*sendFile*/, destPath, null /*filesize*/); - iter(pathToSend, P) { - castS(p, P); - char *sendFile = ssGet(P); + o(netframe,send , sock, buf, bufi); - if (isDirG(p)) { - logD("mkdir in server"); + o(netframe, setCallback, saveReceivedData, receivePath /*context*/); - char *dirname = basename(ssGet(p)); - cleanCharP(dest) = catS(destPath, "/", dirname); + var r = o(netframe,receive, sock); + logVarG(r); - u32 bufi = 0; - u64 *filesize = null; - makeHeader(buf, &bufi, 1 /* command */, svrInfo, ssGet(p), dest, &filesize); + } + else { + // send files - o(netframe,send , sock, buf, bufi); + cleanAllocateSmallArray(pathToSend); - var dir = readDirAllG(rtSmallArrayt, p); - //lv(dir); - if (not isEmptyG(dir)) { - pushG(dir, ssGet(p)); - setNFreeG(recusive, dirname, dir); + rangeFrom(i, 1, ARGC-1) { + if (not isPath(ARGV[i])) { + logE("Incorrect path: "BLD YLW"%s"RST,ARGV[i]); + continue; } - lv(recusive); - continue; + pushG(pathToSend, ARGV[i]); } - // send - u32 bufi = 0; - u64 *filesize = null; - makeHeader(buf, &bufi, 0 /* command */, svrInfo, sendFile, destPath, &filesize); + cleanAllocateSmallDict(recusive); - if (*filesize < (sizeof(buf) - bufi)) { - pError0(bReadFile(sendFile, buf + bufi)); - o(netframe,send , sock, buf, bufi + *filesize); - } - else { - logD("big file"); - u64 szToSend = *filesize; - u8 *b = buf + bufi; - u64 bSz = sizeof(buf) - bufi; - int fd = open(sendFile, O_RDONLY); - do { - if (szToSend <= bSz) { - read(fd, b, szToSend); - o(netframe,send , sock, b, szToSend); - szToSend = 0; - } - else { - read(fd, b, bSz); - if (szToSend == *filesize) { - o(netframe,send , sock, buf, bufi + bSz); - } - else { - o(netframe,send , sock, b, bSz); - } - szToSend -= bSz; - } - } while(szToSend); - close(fd); - } - } // loop on arguments - - - // Send files recursively - - iter(recusive, A) { - cast(smallArrayt*,a,A); - cleanCharP(localPath) = cropElemG(a, rtChar, -1); - lv(localPath); - iter(a, P) { + iter(pathToSend, P) { castS(p, P); char *sendFile = ssGet(P); - cleanCharP(localp) = catS(localPath, "/", ssGet(p)); - if (isDirG(localp)) { + if (isDirG(p)) { logD("mkdir in server"); - cleanCharP(dirname) = catS(iK(recusive), "/", ssGet(p)); + + char *dirname = basename(ssGet(p)); cleanCharP(dest) = catS(destPath, "/", dirname); u32 bufi = 0; u64 *filesize = null; - makeHeader(buf, &bufi, 1 /* command */, svrInfo, localp, dest, &filesize); + makeHeader(buf, &bufi, 1 /* command */, svrInfo, ssGet(p), dest, &filesize); o(netframe,send , sock, buf, bufi); - var dir = readDirAllG(rtSmallArrayt, localp); - lv(dir); + var dir = readDirAllG(rtSmallArrayt, p); + //lv(dir); if (not isEmptyG(dir)) { - pushG(dir, localp); + pushG(dir, ssGet(p)); setNFreeG(recusive, dirname, dir); } - //lv(recusive); + lv(recusive); continue; } - logD("Send files from localPath/p to destPath/iK(recusive)/"); // send - cleanCharP(dest) = catS(destPath, "/", iK(recusive)); - u32 bufi = 0; - u64 *filesize = null; - makeHeader(buf, &bufi, 0 /* command */, svrInfo, localp, dest, &filesize); + u32 bufi = 0; + u64 *filesize = null; + makeHeader(buf, &bufi, 0 /* command */, svrInfo, sendFile, destPath, &filesize); if (*filesize < (sizeof(buf) - bufi)) { - pError0(bReadFile(localp, buf + bufi)); + pError0(bReadFile(sendFile, buf + bufi)); o(netframe,send , sock, buf, bufi + *filesize); } else { @@ -313,7 +211,7 @@ int main(int ARGC, char** ARGV) { u64 szToSend = *filesize; u8 *b = buf + bufi; u64 bSz = sizeof(buf) - bufi; - int fd = open(localp, O_RDONLY); + int fd = open(sendFile, O_RDONLY); do { if (szToSend <= bSz) { read(fd, b, szToSend); @@ -333,14 +231,200 @@ int main(int ARGC, char** ARGV) { } while(szToSend); close(fd); } + } // loop on arguments + + + // Send files recursively + + iter(recusive, A) { + cast(smallArrayt*,a,A); + cleanCharP(localPath) = cropElemG(a, rtChar, -1); + lv(localPath); + iter(a, P) { + castS(p, P); + char *sendFile = ssGet(P); + + cleanCharP(localp) = catS(localPath, "/", ssGet(p)); + if (isDirG(localp)) { + logD("mkdir in server"); + cleanCharP(dirname) = catS(iK(recusive), "/", ssGet(p)); + cleanCharP(dest) = catS(destPath, "/", dirname); + + u32 bufi = 0; + u64 *filesize = null; + makeHeader(buf, &bufi, 1 /* command */, svrInfo, localp, dest, &filesize); + + o(netframe,send , sock, buf, bufi); + + var dir = readDirAllG(rtSmallArrayt, localp); + lv(dir); + if (not isEmptyG(dir)) { + pushG(dir, localp); + setNFreeG(recusive, dirname, dir); + } + //lv(recusive); + continue; + } + + logD("Send files from localPath/p to destPath/iK(recusive)/"); + // send + cleanCharP(dest) = catS(destPath, "/", iK(recusive)); + u32 bufi = 0; + u64 *filesize = null; + makeHeader(buf, &bufi, 0 /* command */, svrInfo, localp, dest, &filesize); + + if (*filesize < (sizeof(buf) - bufi)) { + pError0(bReadFile(localp, buf + bufi)); + o(netframe,send , sock, buf, bufi + *filesize); + } + else { + logD("big file"); + u64 szToSend = *filesize; + u8 *b = buf + bufi; + u64 bSz = sizeof(buf) - bufi; + int fd = open(localp, O_RDONLY); + do { + if (szToSend <= bSz) { + read(fd, b, szToSend); + o(netframe,send , sock, b, szToSend); + szToSend = 0; + } + else { + read(fd, b, bSz); + if (szToSend == *filesize) { + o(netframe,send , sock, buf, bufi + bSz); + } + else { + o(netframe,send , sock, b, bSz); + } + szToSend -= bSz; + } + } while(szToSend); + close(fd); + } + } } - } - lv(recusive); + lv(recusive); + + // last frame size 0 + o(netframe,end, sock); - // last frame size 0 - o(netframe,end, sock); + } // isReceive close(sock); } + + +int packetCount = 0; +int fd = -1; +u64 transferSz = 0; + +bool saveReceivedData(void *receiveB, size_t sz, void *context) { + logVarG(sz); + if (packetCount == 0) { + // header + u8 *command = (u8*) receiveB; + + char *root = context; + + if (*command == 0) { + // receive file + + u16 *filenameLength = (u16*)(receiveB + 9); + // receiveB + 11 = filename + cleanCharP(filename) = malloc(*filenameLength + 1); + filename[*filenameLength] = 0; + memcpy(filename, receiveB + 11, *filenameLength); + u32 bufi = 11 + *filenameLength; + u16 *filemode = (u16*)(receiveB + bufi); + bufi += 2; + u16 *pathLength = (u16*)(receiveB + bufi); + bufi += 2; + cleanCharP(destPath) = null; + if (*pathLength) { + destPath = malloc(*pathLength + 1); + memcpy(destPath, receiveB + bufi, *pathLength); + destPath[*pathLength] = 0; + bufi += *pathLength; + } + // file size + u64 *filesize = (u64*)(receiveB + bufi); + bufi += 8; + + logD("bufi %d fsz %d + %d, filename %s, root %s, destPath %s",bufi, *filesize, bufi + *filesize, filename, root,destPath); + + cleanCharP(path) = null; + + if (not root) { + // save file in current directory + path = strdup(filename); + } + elif (isDir(root)) { + path = catS(root, "/", filename); + } + else { + // root has a filename + path = strdup(root); + } + + pErrorNULL(normalizePathG(&path)); + lv(path); + + if (*filesize <= sz - bufi) { + pError0(writeFile(path, receiveB + bufi, *filesize)); + pError0(fileChmod(path, *filemode)); + } + else { + logD("open fd"); + inc packetCount; + fd = open(path, O_WRONLY | O_CREAT, *filemode); + if (fd == -1) { + logE("cant open %s", path); + ret yes; + } + int r = write(fd, receiveB + bufi, sz - bufi); + if (r == -1) { + logE("cant write %s", path); + ret yes; + } + transferSz = *filesize - (sz - bufi); + } + } + elif (*command == 1) { + // mkdir + TODO("mkdir"); + u16 *filemode = (u16*)(receiveB + 9); + u16 *pathLength = (u16*)(receiveB + 11); + cleanCharP(destPath) = malloc(*pathLength + 1); + memcpy(destPath, receiveB + 13, *pathLength); + destPath[*pathLength] = 0; + char *path = catS(root, "/", destPath); + pErrorNULL(normalizePathG(&path)); + pErrorNULL(trimG(&path)); + logD(BLD GRN"mkdir" RST); + lv(path); + pError0(mkdirParents(path)); + pError0(fileChmod(path, *filemode)); + } + } + else { + // receive large files packetCount > 0 + if (fd == -1) ret yes; + // big file + int r = write(fd, receiveB, sz); + if (r == -1) { + logE("cant write"); + ret yes; + } + if (transferSz <= sz) { + transferSz = 0; + close(fd); + fd = -1; + packetCount = 0; + } + else transferSz -= sz; + } + ret yes; +} // vim: set expandtab ts=2 sw=2: diff --git a/sserver.c b/sserver.c @@ -15,12 +15,15 @@ #include "netFrame.h" #include "shpPackages/short/short.h" +#include "makeHeader.h" #define CONFIG "~/.tuyau/serverConfig.yml" int packetCount = 0; int fd = -1; u64 transferSz = 0; +// client socket +int mysock = 0; smallJsont *tokens = null; @@ -51,7 +54,6 @@ int main(int ARGC, char** ARGV) { int sock; struct sockaddr_in server; - int mysock; int rval; sock = socket(AF_INET, SOCK_STREAM, 0); @@ -194,6 +196,70 @@ void saveReceivedData(void *receiveB, size_t sz) { pError0(mkdirParents(path)); pError0(fileChmod(path, *filemode)); } + elif (*command == 2) { + TODO("send files to client, send back sendFile basename"); + u16 *sendFileLength = (u16*)(receiveB + 9); + cleanCharP(sendFile) = malloc(*sendFileLength + 1); + memcpy(sendFile, receiveB + 11, *sendFileLength); + sendFile[*sendFileLength] = 0; + pErrorNULL(prependG(&sendFile, '/')); + pErrorNULL(prependG(&sendFile, root)); + pErrorNULL(normalizePathG(&sendFile)); + pErrorNULL(trimG(&sendFile)); + lv(sendFile); + cleanCharP(nroot) = normalizePathG(root); + if (not startsWithG(sendFile, nroot)) { + logE("Incorrect path, outside home directory: "BLD YLW"%s"RST,sendFile); + ret; + } + if (not isPath(sendFile)) { + logE("Incorrect path: "BLD YLW"%s"RST,sendFile); + ret; + } + + cleanAllocateNetFrame(netframe); + + u8 buf[1024*1024] = init0Var; + + // send + u32 bufi = 0; + u64 *filesize = null; + makeHeader(buf, &bufi, 0 /* command */, null/*svrInfo no need to authenticate the client*/, sendFile, null/*destPath is already selected in client*/, &filesize); + + if (*filesize < (sizeof(buf) - bufi)) { + pError0(bReadFile(sendFile, buf + bufi)); + o(netframe,send , mysock, buf, bufi + *filesize); + } + else { + logD("big file"); + u64 szToSend = *filesize; + u8 *b = buf + bufi; + u64 bSz = sizeof(buf) - bufi; + int fd = open(sendFile, O_RDONLY); + do { + if (szToSend <= bSz) { + read(fd, b, szToSend); + o(netframe,send , mysock, b, szToSend); + szToSend = 0; + } + else { + read(fd, b, bSz); + if (szToSend == *filesize) { + o(netframe,send , mysock, buf, bufi + bSz); + } + else { + o(netframe,send , mysock, b, bSz); + } + szToSend -= bSz; + } + } while(szToSend); + close(fd); + } + + // last frame size 0 + // close connection + o(netframe,end, mysock); + } } else { // receive large files packetCount > 0