OpenSync 功能說明與系統分析
===
## 專案狀態
- OpenSync on [Github](https://github.com/Neol-d2022/OpenSync-se106a)
- Travis-CI: [![Build Status](https://travis-ci.org/Neol-d2022/OpenSync-se106a.svg?branch=master)](https://travis-ci.org/Neol-d2022/OpenSync-se106a)
## 目錄
[TOC]
## 建置
1. `$ automake` 建置開發環境
2. `$ ./configure` 設定 ==Makefile==
3. - `$ make` 建置專案
- `$ make test` 測試專案
- `$ make dist` 發布專案(建立壓縮檔)
- `$ make distcheck` 測試發布檔案
## 功能說明
- OpenSync的預期功能是達成在兩台不同機器上,同步資料夾的效果。
- ![](https://i.imgur.com/X2afshp.png)
- 相較於Dropbox、MEGASync、Google Drive等,只能同時同步一個資料夾。OpenSync能處理多個同步資料夾,且針對每個資料夾可以個別設定與不同的遠端機器進行同步。
- 本專案為開放原始碼專案。所有原始碼均公開在 [Github](https://github.com/Neol-d2022/OpenSync-se106a) 網站上。
## 系統分析
- 本專案使用 **C 語言** 撰寫。
### 記憶體管理工程 **([已完工](/SyFbyYYAb#%E8%A8%98%E6%86%B6%E9%AB%94%E7%AE%A1%E7%90%86%E6%A8%A1%E7%B5%84--mmh-))**
- 將 ==stdlib.h== 內與記憶體管理相關的函數重新包裝,讓程式要求記憶體,但發生記憶體不夠用的時候,直接終止程式而不是傳回空指標。
- 提供除錯功能。偵測程式有無記憶體洩漏的情況。
- ![](https://i.imgur.com/J1HG9ET.png)
### 檔案樹工程 **([已完工](/SyFbyYYAb#%E6%AA%94%E6%A1%88%E6%A8%B9%E6%A8%A1%E7%B5%84--filetreeh-))**
- 先將同步資料夾的路徑寫死。
- `$ ./syncfolder`
- 程式啟動時讀取目前目錄下的 ==syncfolder== 底下所有的檔案及資料夾,並建立一個樹狀結構來儲存。
- 使用 ==dirent.h== 這個標頭檔,可以列舉資料夾下面的檔案。
- 用`stat()`函數辨別某個路徑是資料夾還是一般檔案。
- ![](https://i.imgur.com/WNHM2vu.png)
### 網路通訊工程
- 使用TCP socket建立連線。
- 定義應用層通訊協定,讓程式能夠互相通訊。
- ![](https://i.imgur.com/DDL51u5.png)
### 檔案樹比對工程 **([已完工](/SyFbyYYAb#%E6%AA%94%E6%A1%88%E6%A8%B9%E6%A8%A1%E7%B5%84--filetreeh-))**
- 讓程式能夠收送檔案樹。
- 穰程式能夠比對出檔案樹的差異。
- ![](https://i.imgur.com/jXyHVHI.png)
### 網路通訊工程-2
- 讓程式能夠收送檔案。
- ![](https://i.imgur.com/nZTcnXI.png)
### 檔案監視工程
- 讓程式能夠監視檔案系統。
- **盡可能跨平台。**
- 一有更動立刻更新本地檔案樹,與遠端交換檔案樹,最後做出差異比對。
- 遠端也有可能隨時送來更新的檔案樹,此時也要做差異比對。
### 檔案同步工程
- 根據差異計算出的差異判斷出:
- 本地檔案新增
- 遠端檔案新增
- 本地檔案修改
- 遠端檔案修改
- 本地擋案移動
- 遠端檔案移動
- ...等
- 並做出適當的動作
- 推送的本地剛新增檔案至遠端
- 從遠端取得剛新增的檔案
- 推送的本地修改後的檔案至遠端
- 從遠端取得修改後的檔案
- 告知遠端(交換檔案樹後即可得知)
- 將本地檔做移動,與遠端同步
- ...等
### 設定檔工程
- 讀取設定檔,程式執行時分配每一個thread監視一個同步資料夾,且每一個資料夾皆可以指定遠端機器路徑。
- 使用POSIX中的`pthread`
### 中央伺服器工程
- 將OpenSync中網路通訊的部分,從Peer-to-Peer的模式變成Client-Server。並在Server記錄每一個同步資料夾的版本變更。
### 客戶憑證工程
- 中央伺服器如何確認客戶端合法使用者?客戶端如何確認伺服器沒有被偽裝?
### 流量加密工程
- 針對透過socket傳輸的資料串流,套用加密演算法,以保護資料機密性。
### 開派對慶祝工程完畢。
## 技術手冊
### 記憶體管理模組 ( ==mm.h== )
1. 名稱
**mm - memory management functions**
1. 使用方法
```c
#include "mm.h"
```
1. 函式列表
```c
/* Request a memory block with s bytes */
void *Mmalloc(size_t s);
/* Relocate memory block p to s bytes */
void *Mrealloc(void *p, size_t s);
/* When the system is out of memory, the functions above will
immediately crash the program, instead of returning NULL */
/* Release memory block p */
void Mfree(void *p);
/* DEBUG. Return the number of allocated memory blocks */
size_t MDebug();
```
1. 測試
- 使用模組測試功能
```c=
#include "mm.h"
#include "mm_test.h" // add this line
int main() {
int mm_test(void); // add this line
return 0;
}
```
- 回傳0代表測試成功,否則代表失敗。
---
### 字串處理模組 ( ==string.h== )
1. 名稱
**string - string operation functions**
1. 使用方法
```c
#include "strings.h"
```
1. 函式列表
```c
/* Duplicate string str. Must be released by call to Mfree(). */
char *SDup(const char *str);
/* Concatenate two strings. Must be released by call to Mfree(). */
char *SConcat(const char *s1, const char *s2);
/* Concatenate multiple strings. Must be released by call to Mfree(). */
char *SMConcat(size_t n, ...);
/* Concatenate multiple strings. Must be released by call to Mfree(). */
char *SMConcatA(size_t n, const char **array);
```
1. 測試
- 使用模組測試功能
```c=
#include "string.h"
#include "strings_test.h" // add this line
int main() {
int string_test(void); // add this line
return 0;
}
```
- 回傳0代表測試成功,否則代表失敗。
---
### 可轉換容器模組 ( ==transformcontainer.h== )
- 此模組是一個子專案,函式輸出及測試請參考它的 [Github](https://github.com/Neol-d2022/transformcontainer)。
---
### 檔案校驗和模組 ( ==crc32.h== )
1. 名稱
**crc32 - file checksum functions**
1. 使用方法
```c
#include "crc32.h"
```
1. 函式列表
```c
/* 計算檔案的校驗和,檔案必須先被開啟。計算完畢之後不會關閉檔案。 */
int Crc32_ComputeFile(FILE *file, uint32_t *outCrc32);
/* 計算一塊記憶體區段的校驗和。 */
/* 呼叫時 inCrc32 應為0 */
uint32_t Crc32_ComputeBuf(uint32_t inCrc32, const void *buf, size_t bufLen);
```
1. 測試
- 本模組沒有測試功能。
注:
參考來源: **網際網路**。
---
### 記憶體處理模組 ( ==mb.h== )
1. 名稱
**mb - memory block management functions**
1. 使用方法
```c
#include "mb.h"
```
1. 結構定義
```c
typedef struct
{
void *ptr; /* 指標。指向一塊記憶體區段的起始位置。 */
size_t size; /* 這塊記憶體區段的大小。 */
} MemoryBlock_t; /* 記憶體區段 */
```
1. 函式列表
```c
/* Duplicate memory block m. Must be released by call to MBfree(). */
void MDup(MemoryBlock_t *dst, const MemoryBlock_t *m);
/* Concatenate two memory blocks. Must be released by call to MBfree(). */
void MConcat(MemoryBlock_t *dst, const MemoryBlock_t *m1, const MemoryBlock_t *m2);
/* Concatenate multiple memory blocks. Must be released by call to MBfree(). */
void MMConcat(MemoryBlock_t *dst, size_t n, ...);
/* Concatenate multiple memory blocks. Must be released by call to MBfree(). */
void MMConcatA(MemoryBlock_t *dst, size_t n, MemoryBlock_t *array);
/* Release a memory block */
void MBfree(MemoryBlock_t *m);
/* Write a 32-bit unsigned integer */
void MWriteU32(void *ptr, uint32_t v);
/* Read a 32-bit unsigned integer */
uint32_t MReadU32(void **ptr);
/* Write a 64-bit unsigned integer */
void MWriteU64(void *ptr, uint64_t v);
/* Read a 64-bit unsigned integer */
uint64_t MReadU64(void **ptr);
/* Write a string to memory block. Must be released by call to MBfree().*/
void MWriteString(MemoryBlock_t *dst, const char *s);
/* Read a string from memory block. Must be released by call to Mfree().
when pass an inappropriate argument, a NULL is returned.*/
char *MReadString(void **ptr, size_t *maxLength);
```
1. 測試
- 本模組沒有測試功能。
---
### 檔案樹模組 ( ==filetree.h== )
1. 名稱
**filetree - file tree management functions**
1. 使用方法
```c
#include "filetree.h"
```
1. 結構定義
```c
typedef struct
{
size_t size; /* 檔案大小 */
time_t timeLastModification; /* 檔案最後修改時間 */
uint32_t crc32; /* 存放檔案CRC32檢查碼 */
uint32_t version; /* 檔案版本 */
} FileNodeTypeFile_t; /* 檔案節點 */
typedef struct
{
struct FileNode_struct_t **children; /* 這個資料結下的節點 */
size_t childrenLen; /* 這個資料結下的節點數量 */
} FileNodeTypeFolder_t; /* 資料夾節點 */
typedef struct FileNode_struct_t
{
union {
FileNodeTypeFile_t file;
FileNodeTypeFolder_t folder;
}; /* 節省記憶體空間 */
char *nodeName; /* 檔名 */
char *fullName; /* 完整路徑 */
struct FileNode_struct_t *parent; /* 上一層的資料夾節點 */
unsigned int flags; /* 旗標 */
} FileNode_t; /* 節點 */
typedef struct
{
char *basePath; /* 檔案樹的路徑 */
FileNode_t ****indexes; /* 總索引表 */
FileNode_t **baseChildren; /* 第一層節點 */
FileNode_t **totalFiles; /* 所有檔案 */
FileNode_t **totalFolders; /* 所有資料夾 */
size_t baseChildrenLen; /* 第一層節點數量 */
size_t totalFilesLen; /* 所有檔案數量 */
size_t totalFoldersLen; /* 所有資料夾數量 */
} FileTree_t; /* 檔案樹 */
typedef struct
{
FileNode_t *from; /* 被更改的節點 */
FileNode_t *to; /* 更改後的節點 */
} FileNodeDiff_t; /* 節點差異結構 */
```
1. 函式列表
```c
/* Initialize a file tree */
void FileTreeInit(FileTree_t *t);
/* Destroy a file tree */
void FileTreeDeInit(FileTree_t *t);
/* Set Base Path */
void FileTreeSetBasePath(FileTree_t *t, const char *basePath);
/* Scan And Create File Tree */
int FileTreeScan(FileTree_t *t);
/* DEBUG. Print file tree */
void FileTreeDebugPrint(FileTree_t *t);
/* File Tree to Memory Block */
void FileTreeToMemoryblock(FileTree_t *t, MemoryBlock_t *mb);
/* Memory Block to File Tree, Return NULL if invalid */
FileTree_t *FileTreeFromMemoryBlock(MemoryBlock_t *mb, const char *parentPath);
/* Compute CRC32 of every files under the tree */
int FileTreeComputeCRC32(FileTree_t *t);
/* Compute Difference */
unsigned int FileTreeDiff(FileTree_t *t_old,
FileTree_t *t_new,
FileNodeDiff_t ***diff,
size_t *diffLen);
/* Release Object */
void FileNodeDiffRelease(FileNodeDiff_t **diff, size_t len);
/* DEBUG. Print file node difference */
void FileNodeDiffDebugPrint(FileNodeDiff_t **diff, size_t len);
/* */
FileTree_t *FileTreeFromFile(const char *filename, const char *syncdir);
/* */
int FileTreeToFile(const char *filename, FileTree_t *ft);
```
1. 測試
- 使用模組測試功能
```c=
#include "string.h"
#include "filetree_test.h" // add this line
int main() {
int filetree_test(void); // add this line
return 0;
}
```
- 回傳0代表測試成功,否則代表失敗。
6. 補充
- 如果`FileTreeSetBasePath()`遇到不可存取的資料夾/檔案(通常是權限不足),或是無法辨別型態(既不是檔案也不是資料夾),則會忽略那個檔名並繼續,並會回傳 **最後一次** 發生的`errno`。
- 正常結束則回傳0。
- `FileTreeToMemoryblock()` 將檔案樹以一塊記憶體區段來表示,以便日後網路通訊使用。
- `MemoryBlock_t *mb` 可不必初始化;**若已經初始化,則必須先釋放。**
- `FileTreeFromMemoryBlock()` 可將一塊記憶體區段轉換成檔案樹。
- `const char *parentPath` 必須指定檔案樹的**根**資料夾路徑。
- 若此記憶體區段無法表示成檔案樹,則會回傳空指標。
---
### 專案描述模組 ( ==config.h== )
1. 名稱
**config - package description definitions**
1. 描述
- 定義專案名稱及版本號碼。
```c
#define PACKAGE_NAME "OpenSync"
#define PACKAGE_VERSION "version_number"
```
1. 測試
- 本模組沒有測試功能。
---
### 設定檔模組 ( ==configurer.h== )
1. 名稱
**configurer - configure file loader for main program initialization**
1. 使用方法
```c
#include "configurer.h"
```
1. 結構定義
```c
typedef struct
{
SynchronizationProtocols_t *sp; // 執行緒同步物件
char *workingFolder; // 客戶端工作資料夾(檔名自動產生)
char *basePath; // 客戶端同步資料夾
char *remoteIP; // 伺服器IP位置
unsigned int remotePort; // 伺服器port號碼
unsigned int magicNumber; // 認證用magic number(防止設定檔誤設)
} SynchronizationClient_t; // 客戶端
typedef struct
{
SynchronizationProtocols_t *sp; // 執行緒同步物件
char *workingFolder; // 伺服器工作資料夾(檔名自動產生)
char *basePath; // 伺服器同步資料夾
unsigned int listeningPort; // 監聽port號碼
unsigned int magicNumber; // 認證用magic number(防止設定檔誤設)
} SynchronizationServer_t; // 伺服器
typedef struct
{
SynchronizationProtocols_t *sp; // 執行緒同步物件
size_t nClients; // 要管理的客戶端數量
size_t nServers; // 要管理的伺服器數量
SynchronizationClient_t **clients; // 客戶端
SynchronizationServer_t **servers; // 伺服器
pthread_t *clientThreads; // 客戶端執行緒
pthread_t *serverThreads; // 伺服器執行緒
} Configuration_t; // 客戶端及伺服器設定
```
1. 函式列表
```c
/* Read configuration file */
int ConfigurerReadConfig(const char *filename, Configuration_t *c);
/* Release all resources which associated with the configuration */
void ConfigurerRelease(Configuration_t *c);
/* Print the configuration */
void ConfigurerDebugPrint(Configuration_t *c);
/* Start the program by the configuration */
int ConfigurerStartup(Configuration_t *c);
```
1. 測試
- 使用模組測試功能
```c=
#include "string.h"
#include "configurer_test.h" // add this line
int main() {
int configurer_test(void); // add this line
return 0;
}
```
- 回傳0代表測試成功,否則代表失敗。
---
### 網路通訊模組 ( ==xsocket.h== )
1. 名稱
**xsocket - network socket functions**
1. 使用方法
```c
#include "xsocket.h"
```
1. 函式列表
```c
/* Socket library initialization */
int socketLibInit(void);
/* Socket library deinitialization */
int socketLibDeInit(void);
/* Close network socket */
int socketClose(SOCKET sock);
```
1. 測試
- 本模組沒有測試功能。
---
### 模組 ( ==.h== )
1. 名稱
****
1. 使用方法
```c
#include ".h"
```
1. 結構定義
```c
```
1. 函式列表
```c
```
1. 測試
- 使用模組測試功能
```c=
#include "string.h"
#include ".h" // add this line
int main() {
int _test(void); // add this line
return 0;
}
```
- 回傳0代表測試成功,否則代表失敗。
- 本模組沒有測試功能。