文件同步工具
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

226 lines
5.7 KiB

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <libgen.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "ftree.h"
#include "hash.h"
int depth = 0;
#define MAXDATASIZE 100 /* max number of bytes we can get at once */
/*
* Concatenates child path to parent path.
*/
void complete_path(const char *parent, const char *child, char *dest) {
strcpy(dest, parent);
strcat(dest, "/");
strcat(dest, child);
}
/*
* Creates and returns a new socket connection.
*/
int accept_connnection(char *host, unsigned short port) {
int sockfd;
struct hostent *he;
struct sockaddr_in their_addr; /* connector's address information */
if ((he=gethostbyname(host)) == NULL) { /* get the host info */
perror("gethostbyname");
exit(1);
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
their_addr.sin_family = AF_INET; /* host byte order */
their_addr.sin_port = htons(port); /* short, network byte order */
their_addr.sin_addr = *((struct in_addr *)he->h_addr);
bzero(&(their_addr.sin_zero), 8); /* zero the rest of the struct */
if (connect(sockfd, (struct sockaddr *)&their_addr,
sizeof(struct sockaddr)) == -1) {
perror("connect");
exit(1);
}
return sockfd;
}
/*
* Copies file tree with root based at src to server.
* r_src is the root for the tree on the server.
*/
int rcopy_client(char *src, char *host, unsigned short port, char *r_src, int type) {
int sockfd = accept_connnection(host, port);
int fcalls = 0; // keep track of children.
int final = 0; // return value.
// 文件、文件夹的属性
struct stat properties;
// 请求结构体,包含路径信息等
struct request r;
// lstat 获取文件属性,比如文件权限,文件属主,文件大小等属性。
if (lstat(src, &properties) == -1) {
perror("client: lstat");
exit(1);
}
if (!S_ISLNK(properties.st_mode)) { // skip links.
strcpy(r.path, r_src);
r.mode = properties.st_mode;
r.size = properties.st_size;
// 判断是否目录
if (!S_ISDIR(properties.st_mode)) {
if (type == 0) {
r.type = REGFILE;
} else {
r.type = TRANSFILE;
}
FILE *in; // 文件指针
if ((in = fopen(src, "rb")) != NULL) {
hash(r.hash, in);
fclose(in);
} else {
perror("client: fopen");
exit(1);
}
} else {
r.type = REGDIR;
}
int t, m;
t = htonl(r.type);
m = htonl(r.mode);
if (write(sockfd, &t, sizeof(int)) == -1) {
perror("client: writing type");
exit(1);
}
if (write(sockfd, &r.path, MAXPATH) == -1) {
perror("client: writing path");
exit(1);
}
if (write(sockfd, &m, sizeof(int)) == -1) {
perror("client: writing mode");
exit(1);
}
if (write(sockfd, &r.size, sizeof(int)) == -1) {
perror("client: writing size");
exit(1);
}
if (write(sockfd, &r.hash, BLOCKSIZE) == -1) {
perror("client: writing hash");
exit(1);
}
if (r.type == TRANSFILE) {
FILE *out;
if ((out = fopen(src, "rb")) != NULL) {
int s = 0;
char byte;
while ((fread(&byte, 1, 1, out) == 1) && s++ < r.size) {
write(sockfd, &byte, 1);
}
fclose(out);
} else {
perror("client: fopen");
exit(1);
}
}
int status, len;
len = read(sockfd, &status, sizeof(int));
if (len != sizeof(int)) {
perror("client: reading from socket");
exit(1);
} else {
if (status == SENDFILE) {
pid_t pid = fork();
if (pid == 0) {
close(sockfd); // child will initate a new connection.
depth++;
rcopy_client(src, host, port, r_src, TRANSFILE);
} else {
fcalls++;
}
}
if (status == OK) {
// 如果是目录
if (S_ISDIR(properties.st_mode)) {
DIR *curr;
if ((curr = opendir(src)) == NULL) {
perror("client: opendir");
exit(1);
}
struct dirent *child;
// 递归调用目录
while ((child = readdir(curr)) != NULL) {
if (strncmp(child->d_name, ".", 1) != 0) {
pid_t pid = fork();
if (pid == 0) {
depth++;
char client_child_path[MAXPATH];
char server_child_path[MAXPATH];
complete_path(src, child->d_name, client_child_path);
complete_path(r_src, child->d_name, server_child_path);
close(sockfd);
rcopy_client(client_child_path, host, port, server_child_path, 0);
} else {
fcalls++;
}
}
}
}
// 文件的执行结果
printf("File:%s,size:%dkb has been send successfully.\n",r.path,r.size);
}
if (status == ERROR) {
fprintf(stderr, "client: error occured with %s\n", src);
final = 1;
}
}
}
close(sockfd);
int status;
char pid;
while (fcalls > 0) {
wait(&status);
if (WIFEXITED(status)) {
pid = WEXITSTATUS(status);
if (!final) {
final = pid;
}
} else {
perror("client: exit");
exit(1);
}
--fcalls;
}
if (depth == 0) {
return final;
} else {
exit(final);
}
}